defmodule Object.LLMIntegration do @moduledoc """ Enhanced LLM integration for AAOS objects using DSPy. This module enables objects to: 1. Respond to messages using LLM-powered natural language 2. Generate contextual responses based on object state and history 3. Adapt their communication style based on interaction patterns 4. Use structured reasoning for complex decisions Usage Examples: # Basic LLM response generation {:ok, response} = Object.LLMIntegration.generate_response(object, incoming_message) # Contextual conversation {:ok, response} = Object.LLMIntegration.conversational_response(object, message, conversation_history) # Goal-oriented reasoning {:ok, plan} = Object.LLMIntegration.reason_about_goal(object, goal, current_situation) """ alias Object.DSPyBridge @doc """ Generates an LLM-powered response to an incoming message. ## Parameters - `object`: The object generating the response - `message`: Incoming message to respond to - `opts`: Options like `:style`, `:max_length` ## Returns `{:ok, response, updated_object}` with generated response and updated object state ## Examples iex> Object.LLMIntegration.generate_response(object, message) {:ok, %{content: "I understand your request...", tone: "helpful"}, updated_object} """ def generate_response(object, message, opts \\ []) do signature = create_response_signature(object, message, opts) case DSPyBridge.execute_signature(object.id, signature) do {:ok, result} -> response = %{ content: result.response_text, tone: result.tone, intent: result.intent, confidence: result.confidence, follow_up_suggested: result.suggests_follow_up, metadata: %{ generated_at: DateTime.utc_now(), model_used: result.model_info, reasoning_chain: result.reasoning_steps } } # Update object's interaction history with LLM response updated_object = Object.interact(object, %{ type: :llm_response, original_message: message, generated_response: response, success: true }) {:ok, response, updated_object} {:error, _reason} -> # Fallback to rule-based response fallback_response = generate_fallback_response(object, message) {:ok, fallback_response, object} end end @doc """ Generates contextual conversation responses maintaining conversation state. ## Parameters - `object`: The object in conversation - `message`: Current message to respond to - `conversation_history`: Previous messages in conversation (default: []) ## Returns `{:ok, response, updated_object}` with conversational response """ def conversational_response(object, message, conversation_history \\ []) do # Extract conversation context context = extract_conversation_context(conversation_history) personality = get_object_personality(object) signature = %{ description: "Generate a conversational response maintaining context and personality", inputs: [ current_message: message.content, sender_info: message.sender, conversation_context: context, object_personality: personality, object_state: summarize_object_state(object), interaction_goals: object.goal ], outputs: [ response_text: "Natural language response", emotional_tone: "Emotional tone of the response", conversation_direction: "How this response guides the conversation", relationship_impact: "How this response affects the relationship", memory_updates: "What should be remembered from this exchange" ], instructions: """ Generate a natural, contextually appropriate response that: 1. Acknowledges the conversation history and context 2. Reflects the object's personality and current state 3. Advances the conversation toward mutual goals 4. Maintains appropriate emotional tone 5. Builds positive relationships while being authentic Consider the object's role, capabilities, and current objectives. """ } case DSPyBridge.execute_signature(object.id, signature) do {:ok, result} -> # Update conversation memory updated_object = update_conversation_memory(object, message, result) response = %{ content: result.response_text, tone: result.emotional_tone, conversation_direction: result.conversation_direction, relationship_impact: result.relationship_impact, memory_updates: result.memory_updates, timestamp: DateTime.utc_now() } {:ok, response, updated_object} {:error, reason} -> {:error, "Failed to generate conversational response: #{reason}"} end end @doc """ Uses LLM reasoning for complex goal-oriented decisions. ## Parameters - `object`: The reasoning object - `goal`: Goal to reason about - `current_situation`: Current state and context - `constraints`: Limitations to consider (default: []) ## Returns `{:ok, reasoning_result, updated_object}` with detailed reasoning analysis """ def reason_about_goal(object, goal, current_situation, constraints \\ []) do signature = %{ description: "Reason about achieving a goal given current situation and constraints", inputs: [ goal_description: goal, current_situation: current_situation, object_capabilities: object.methods, available_resources: Map.get(object.state, :resources, %{}), constraints: constraints, past_experience: extract_relevant_experience(object, goal) ], outputs: [ reasoning_chain: "Step-by-step reasoning process", action_plan: "Concrete steps to achieve the goal", resource_requirements: "Resources needed for each step", risk_assessment: "Potential risks and mitigation strategies", success_probability: "Estimated probability of success", alternative_approaches: "Alternative methods if primary plan fails" ], instructions: """ Provide comprehensive reasoning about achieving the specified goal: 1. Analyze the current situation and identify key factors 2. Break down the goal into achievable sub-goals 3. Develop a step-by-step action plan 4. Assess resource requirements and availability 5. Identify potential risks and mitigation strategies 6. Estimate success probability based on available information 7. Suggest alternative approaches for resilience Be specific, practical, and consider the object's actual capabilities. """ } case DSPyBridge.execute_signature(object.id, signature) do {:ok, result} -> reasoning_result = %{ reasoning_chain: parse_reasoning_chain(result.reasoning_chain), action_plan: parse_action_plan(result.action_plan), resource_requirements: result.resource_requirements, risk_assessment: result.risk_assessment, success_probability: parse_probability(result.success_probability), alternatives: result.alternative_approaches, generated_at: DateTime.utc_now() } # Update object's meta-learning with reasoning experience updated_object = Object.MetaDSL.execute( object.meta_dsl, :learn, object, {:reasoning_experience, reasoning_result} ) case updated_object do {_meta_dsl_result, updated_meta_dsl} -> final_object = %{object | meta_dsl: updated_meta_dsl} {:ok, reasoning_result, final_object} _ -> {:ok, reasoning_result, object} end {:error, reason} -> {:error, "Goal reasoning failed: #{reason}"} end end @doc """ Enables objects to engage in collaborative reasoning with other objects. ## Parameters - `objects`: List of objects participating in reasoning - `shared_problem`: Problem to solve collaboratively - `collaboration_type`: Type of collaboration (`:consensus`, `:negotiation`, etc.) ## Returns `{:ok, collaboration_result}` with synthesized solution and role assignments """ def collaborative_reasoning(objects, shared_problem, collaboration_type \\ :consensus) do signature = %{ description: "Facilitate collaborative reasoning between multiple objects", inputs: [ problem_description: shared_problem, participating_objects: Enum.map(objects, &summarize_object_for_collaboration/1), collaboration_type: collaboration_type, individual_perspectives: gather_individual_perspectives(objects, shared_problem) ], outputs: [ synthesis: "Synthesized understanding incorporating all perspectives", collaborative_solution: "Proposed solution leveraging collective capabilities", role_assignments: "Specific roles for each participating object", coordination_plan: "How objects should coordinate their efforts", consensus_level: "Level of agreement achieved among objects" ], instructions: """ Facilitate collaborative reasoning by: 1. Analyzing each object's unique perspective and capabilities 2. Identifying complementary strengths and potential conflicts 3. Synthesizing insights into a coherent understanding 4. Developing a solution that leverages collective capabilities 5. Assigning specific roles based on object strengths 6. Creating a coordination plan for implementation 7. Assessing the level of consensus achieved Ensure the solution is practical and respects each object's constraints. """ } # Use the first object's DSPy bridge for coordination coordinator_object = hd(objects) case DSPyBridge.execute_signature(coordinator_object.id, signature) do {:ok, result} -> collaboration_result = %{ synthesis: result.synthesis, solution: parse_collaborative_solution(result.collaborative_solution), role_assignments: parse_role_assignments(result.role_assignments, objects), coordination_plan: result.coordination_plan, consensus_level: result.consensus_level, participants: Enum.map(objects, & &1.id), timestamp: DateTime.utc_now() } {:ok, collaboration_result} {:error, reason} -> {:error, "Collaborative reasoning failed: #{reason}"} end end @doc """ Creates a custom DSPy signature for specific object interactions. ## Parameters - `name`: Name for the signature - `description`: Description of what the signature does - `inputs`: List of input fields - `outputs`: List of output fields - `instructions`: Instructions for the LLM ## Returns Signature specification map """ def create_custom_signature(name, description, inputs, outputs, instructions) do %{ name: name, description: description, inputs: inputs, outputs: outputs, instructions: instructions, created_at: DateTime.utc_now() } end @doc """ Registers a custom signature for reuse across objects. ## Parameters - `signature`: Signature specification to register ## Returns `{:ok, signature_name}` on successful registration """ def register_signature(signature) do # Ensure ETS table exists case :ets.whereis(:custom_signatures) do :undefined -> :ets.new(:custom_signatures, [:set, :public, :named_table]) _ -> :ok end # Store in ETS table for quick access :ets.insert(:custom_signatures, {signature.name, signature}) {:ok, signature.name} end # Private helper functions defp create_response_signature(object, message, opts) do response_style = Keyword.get(opts, :style, :professional) max_length = Keyword.get(opts, :max_length, 200) %{ description: "Generate an appropriate response to an incoming message", inputs: [ message_content: message.content, sender_id: message.sender, object_state: summarize_object_state(object), object_role: get_object_role(object), recent_interactions: get_recent_interactions(object), response_style: response_style ], outputs: [ response_text: "The response message content", tone: "Emotional tone of the response", intent: "Primary intent of the response", confidence: "Confidence in this response", suggests_follow_up: "Whether a follow-up interaction is suggested" ], instructions: """ Generate a response that: 1. Directly addresses the message content 2. Reflects the object's current state and role 3. Maintains appropriate tone for the context 4. Is concise (under #{max_length} words) but complete 5. Aligns with the object's goals and capabilities Response style: #{response_style} """ } end defp generate_fallback_response(object, _message) do %{ content: "I received your message and am processing it. My current state: #{object.subtype}", tone: "neutral", intent: "acknowledgment", confidence: 0.5, follow_up_suggested: false, metadata: %{ fallback: true, generated_at: DateTime.utc_now() } } end defp extract_conversation_context(history) do recent_messages = Enum.take(history, -5) %{ message_count: length(history), recent_topics: extract_topics(recent_messages), sentiment_trend: analyze_sentiment_trend(recent_messages), interaction_frequency: calculate_interaction_frequency(history) } end defp get_object_personality(object) do # Extract personality traits from object's behavior patterns %{ communication_style: infer_communication_style(object), cooperation_tendency: analyze_cooperation_patterns(object), problem_solving_approach: analyze_problem_solving(object), emotional_expression: analyze_emotional_patterns(object) } end defp summarize_object_state(object) do %{ type: object.subtype, current_energy: Map.get(object.state, :energy, "unknown"), active_goals: summarize_goals(object.goal), recent_performance: get_recent_performance(object), available_methods: object.methods } end defp update_conversation_memory(object, message, llm_result) do memory_update = %{ type: :conversation_memory, message: message, response: llm_result, relationship_impact: llm_result.relationship_impact, timestamp: DateTime.utc_now() } Object.interact(object, memory_update) end defp extract_relevant_experience(object, goal) do # Filter interaction history for relevant experiences object.interaction_history |> Enum.filter(fn interaction -> case interaction.data do %{goal: past_goal} -> goal_similarity(goal, past_goal) > 0.5 _ -> false end end) |> Enum.take(-3) # Last 3 relevant experiences end defp parse_reasoning_chain(reasoning_text) do # Parse structured reasoning from LLM output reasoning_text |> String.split("\n") |> Enum.filter(&(String.trim(&1) != "")) |> Enum.with_index(1) |> Enum.map(fn {step, index} -> %{step: index, reasoning: String.trim(step)} end) end defp parse_action_plan(plan_text) do # Parse action plan into structured format plan_text |> String.split("\n") |> Enum.filter(&String.contains?(&1, ".")) |> Enum.map(fn step -> [number, action] = String.split(step, ".", parts: 2) %{step: String.to_integer(String.trim(number)), action: String.trim(action)} end) end defp parse_probability(prob_text) do # Extract probability from text case Regex.run(~r/(\d+(?:\.\d+)?)%?/, prob_text) do [_, number] -> {prob, _} = Float.parse(number) if prob > 1, do: prob / 100, else: prob _ -> 0.5 # Default probability end end defp gather_individual_perspectives(objects, problem) do for object <- objects do # Get each object's perspective on the problem case reason_about_goal(object, problem, summarize_object_state(object)) do {:ok, reasoning, _} -> %{object_id: object.id, perspective: reasoning} _ -> %{object_id: object.id, perspective: "No perspective available"} end end end defp summarize_object_for_collaboration(object) do %{ id: object.id, type: object.subtype, capabilities: object.methods, strengths: identify_strengths(object), current_load: assess_current_load(object) } end defp parse_collaborative_solution(solution_text) do # Parse the collaborative solution into actionable components %{ overview: extract_overview(solution_text), key_components: extract_components(solution_text), implementation_steps: extract_steps(solution_text) } end defp parse_role_assignments(assignments_text, objects) do # Parse role assignments and match to objects for object <- objects do role = extract_role_for_object(assignments_text, object.id) %{object_id: object.id, assigned_role: role} end end # Simplified helper implementations defp extract_topics(_messages), do: ["general", "collaboration"] defp analyze_sentiment_trend(_messages), do: "neutral" defp calculate_interaction_frequency(history), do: length(history) / max(1, div(length(history), 10)) defp infer_communication_style(_object), do: "direct" defp analyze_cooperation_patterns(_object), do: "collaborative" defp analyze_problem_solving(_object), do: "systematic" defp analyze_emotional_patterns(_object), do: "stable" defp summarize_goals(goal_function), do: "#{goal_function}" defp get_recent_performance(_object), do: "stable" defp goal_similarity(_goal1, _goal2), do: 0.7 defp identify_strengths(object), do: object.methods defp assess_current_load(_object), do: "moderate" defp extract_overview(text), do: String.slice(text, 0..100) defp extract_components(text), do: String.split(text, ",") |> Enum.take(3) defp extract_steps(text), do: String.split(text, "\n") |> Enum.take(5) defp extract_role_for_object(_text, object_id), do: "participant_#{object_id}" defp get_object_role(object), do: object.subtype defp get_recent_interactions(object), do: Enum.take(object.interaction_history, -3) end