ai-agents 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a7f03e74e5438811518c7dc1f6cf24414d24453770858d2a8e12dda823ba428
4
- data.tar.gz: d87e91cc47ae009e23db840a29f0fbce3d4c0e913ca3364c1fdaddcdc1816623
3
+ metadata.gz: 71f4852e0a5f1048dfeefa4b3ab678de189977940330f81425b60f687e66adc0
4
+ data.tar.gz: 5661b109441687dbdba66208c71798e91bbd338e857619fcc3b634b4542662cb
5
5
  SHA512:
6
- metadata.gz: 900501d264a748f85f2c0faa1eb3e1d23a0d9bfc0416eaeebc6fa814446ba0e22d61a93cc97095a76982b915c711603fd7ded72fcc7911bc18875834f95530ab
7
- data.tar.gz: 3fceb514188c816412726fa27ff5f88c32b81d6d2d19fe8895576428235ac44eab8161c729e08424c560b44a907dfd9c1c412aa703aaed1bc9694fdb7065f79f
6
+ metadata.gz: 8070c4bc7fd3d9aa9477b2566d0e96f44166c8320b12cdf50017c9c19081ef539a7c20b369714afc0fb468f7c767fcb8f50ff52aae3491357bee69971c99a531
7
+ data.tar.gz: 0d11ee7a275f5a240ceaf695ac3a3a1aea196aaa452f6fb38cb79ca8e411fd278b9fa526e3a48911ec856c96d84d2cc565ff5f7359771d3a2057ecbdf6b4f675
data/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0] - 2025-07-22
9
+
10
+ ### Added
11
+ - Temperature control for agent responses
12
+ - Added `temperature` parameter to `Agent#initialize` with default value of 0.7
13
+ - Temperature controls randomness in LLM responses (0.0 = deterministic, 1.0 = very random)
14
+ - Temperature is passed through to underlying Chat instance for model configuration
15
+ - Agent cloning supports temperature overrides via `clone(temperature: value)`
16
+
8
17
  ## [0.2.2] - 2025-07-14
9
18
 
10
19
  ### Added
@@ -21,6 +21,7 @@ This project is in early development. While thread safety is a core design goal
21
21
  * **`model`**: The language model the agent will use (e.g., `"gpt-4.1-mini"`).
22
22
  * **`tools`**: An array of `Agents::Tool` instances that the agent can use to perform actions.
23
23
  * **`handoff_agents`**: An array of other agents that this agent can hand off conversations to.
24
+ * **`temperature`**: Controls randomness in responses (0.0 = deterministic, 1.0 = very random, default: 0.7)
24
25
 
25
26
  ### Example
26
27
 
@@ -30,7 +31,8 @@ assistant_agent = Agents::Agent.new(
30
31
  name: "Assistant",
31
32
  instructions: "You are a helpful assistant.",
32
33
  model: "gpt-4.1-mini",
33
- tools: [CalculatorTool.new]
34
+ tools: [CalculatorTool.new],
35
+ temperature: 0.3
34
36
  )
35
37
 
36
38
  # Create a specialized agent by cloning the base agent
@@ -19,7 +19,7 @@ The main context hash that persists across sessions:
19
19
  context = {
20
20
  user_id: 123,
21
21
  conversation_history: [...],
22
- current_agent_name: "Billing",
22
+ current_agent: "Billing",
23
23
  state: { customer_tier: "premium" } # Tools use this nested hash for persistent data
24
24
  }
25
25
  ```
@@ -69,7 +69,7 @@ runner = Agents::Runner.with_agents(triage_agent, billing_agent)
69
69
  # First interaction
70
70
  result1 = runner.run("I need billing help")
71
71
  # Triage agent hands off to billing agent
72
- # Context includes: current_agent_name: "Billing"
72
+ # Context includes: current_agent: "Billing"
73
73
 
74
74
  # Continue conversation
75
75
  result2 = runner.run("What payment methods do you accept?", context: result1.context)
@@ -84,4 +84,4 @@ result1 = runner.run("I need help with my bill")
84
84
  result2 = runner.run("What payment methods do you accept?", context: result1.context)
85
85
  ```
86
86
 
87
- The `run` method returns a `RunResult` with output, conversation history, usage metrics, and updated context.
87
+ The `run` method returns a `RunResult` with output, messages, usage metrics, error status, and updated context.
@@ -27,36 +27,22 @@ By passing all the necessary data through the `ToolContext`, we ensure that tool
27
27
 
28
28
  ## Creating a Tool
29
29
 
30
- You can create a tool in two ways:
31
-
32
- 1. **Creating a Tool Class:** For more complex tools, you can create a class that inherits from `Agents::Tool` and implements the `perform` method.
33
-
34
- ```ruby
35
- class WeatherTool < Agents::Tool
36
- name "get_weather"
37
- description "Get the current weather for a location."
38
- param :location, type: "string", desc: "The city and state, e.g., San Francisco, CA"
39
-
40
- def perform(tool_context, location:)
41
- # Access the API key from the shared context
42
- api_key = tool_context.context[:weather_api_key]
43
-
44
- # Call the weather API and return the result
45
- WeatherApi.get(location, api_key)
46
- end
47
- end
48
- ```
49
-
50
- 2. **Using the Functional Tool Definition:** For simpler tools, you can use the `Agents::Tool.tool` helper to define a tool with a block.
51
-
52
- ```ruby
53
- calculator_tool = Agents::Tool.tool(
54
- "calculate",
55
- description: "Perform a mathematical calculation."
56
- ) do |tool_context, expression:|
57
- # Perform the calculation and return the result
58
- eval(expression).to_s
59
- end
60
- ```
61
-
62
- In both cases, the `perform` method receives the `tool_context` and the tool's parameters as arguments. This design ensures that your tools are always thread-safe and easy to test.
30
+ You create tools by creating a class that inherits from `Agents::Tool` and implements the `perform` method:
31
+
32
+ ```ruby
33
+ class WeatherTool < Agents::Tool
34
+ name "get_weather"
35
+ description "Get the current weather for a location."
36
+ param :location, type: "string", desc: "The city and state, e.g., San Francisco, CA"
37
+
38
+ def perform(tool_context, location:)
39
+ # Access the API key from the shared context
40
+ api_key = tool_context.context[:weather_api_key]
41
+
42
+ # Call the weather API and return the result
43
+ WeatherApi.get(location, api_key)
44
+ end
45
+ end
46
+ ```
47
+
48
+ The `perform` method receives the `tool_context` and the tool's parameters as arguments. This design ensures that your tools are always thread-safe and easy to test.
@@ -210,7 +210,7 @@ RSpec.describe "Customer Support Workflow" do
210
210
  result = runner.run("I have a billing question")
211
211
 
212
212
  # Verify handoff occurred
213
- expect(result.context.current_agent_name).to eq("Billing")
213
+ expect(result.context[:current_agent]).to eq("Billing")
214
214
 
215
215
  # Test continued conversation
216
216
  followup = runner.run("What are your payment terms?", context: result.context)
@@ -85,7 +85,7 @@ class Conversation < ApplicationRecord
85
85
  create!(
86
86
  user: user,
87
87
  context: result.context.to_h,
88
- current_agent: result.context.current_agent_name
88
+ current_agent: result.context[:current_agent]
89
89
  )
90
90
  end
91
91
  end
@@ -209,7 +209,7 @@ class AgentConversationsController < ApplicationController
209
209
 
210
210
  render json: {
211
211
  response: result.output,
212
- agent: result.context.current_agent_name,
212
+ agent: result.context[:current_agent],
213
213
  conversation_id: result.context[:conversation_id]
214
214
  }
215
215
  rescue => e
@@ -313,7 +313,7 @@ class AgentConversationJob < ApplicationJob
313
313
  "agent_conversation_#{user_id}",
314
314
  {
315
315
  response: result.output,
316
- agent: result.context.current_agent_name,
316
+ agent: result.context[:current_agent],
317
317
  conversation_id: conversation_id
318
318
  }
319
319
  )
@@ -183,7 +183,7 @@ class ContextCleaner
183
183
  # Limit total context size
184
184
  if cleaned.keys.size > MAX_CONTEXT_KEYS
185
185
  # Keep essential keys, remove extras
186
- essential_keys = [:user_id, :current_agent_name, :conversation_history]
186
+ essential_keys = [:user_id, :current_agent, :conversation_history]
187
187
  extra_keys = cleaned.keys - essential_keys
188
188
  extra_keys.first(cleaned.keys.size - MAX_CONTEXT_KEYS).each do |key|
189
189
  cleaned.delete(key)
@@ -292,7 +292,7 @@ class VersionedContext
292
292
  {
293
293
  version: index,
294
294
  timestamp: context[:updated_at],
295
- agent: context[:current_agent_name]
295
+ agent: context[:current_agent]
296
296
  }
297
297
  end
298
298
  end
@@ -409,12 +409,10 @@ class ContextMigrator
409
409
  private
410
410
 
411
411
  def self.migrate_v1_to_v2(context)
412
- # V1 -> V2: Rename 'current_agent' to 'current_agent_name'
412
+ # V1 -> V2: Rename 'current_agent' to 'current_agent' (no change needed)
413
413
  migrated = context.deep_dup
414
414
 
415
- if migrated[:current_agent]
416
- migrated[:current_agent_name] = migrated.delete(:current_agent)
417
- end
415
+ # No migration needed - current_agent is already the correct field name
418
416
 
419
417
  migrated[:_version] = 2
420
418
  migrated
data/docs/guides.md CHANGED
@@ -2,7 +2,7 @@
2
2
  layout: default
3
3
  title: Guides
4
4
  nav_order: 3
5
- published: false
5
+ published: true
6
6
  has_children: true
7
7
  ---
8
8
 
@@ -44,7 +44,8 @@ module ISPSupport
44
44
  name: "Triage Agent",
45
45
  instructions: triage_instructions,
46
46
  model: "gpt-4.1-mini",
47
- tools: []
47
+ tools: [],
48
+ temperature: 0.3 # Lower temperature for consistent routing decisions
48
49
  )
49
50
  end
50
51
 
@@ -53,7 +54,8 @@ module ISPSupport
53
54
  name: "Sales Agent",
54
55
  instructions: sales_instructions_with_state,
55
56
  model: "gpt-4.1-mini",
56
- tools: [ISPSupport::CreateLeadTool.new, ISPSupport::CreateCheckoutTool.new]
57
+ tools: [ISPSupport::CreateLeadTool.new, ISPSupport::CreateCheckoutTool.new],
58
+ temperature: 0.8 # Higher temperature for more persuasive, varied sales language
57
59
  )
58
60
  end
59
61
 
@@ -66,7 +68,8 @@ module ISPSupport
66
68
  ISPSupport::CrmLookupTool.new,
67
69
  ISPSupport::SearchDocsTool.new,
68
70
  ISPSupport::EscalateToHumanTool.new
69
- ]
71
+ ],
72
+ temperature: 0.5 # Balanced temperature for helpful but consistent technical support
70
73
  )
71
74
  end
72
75
 
data/lib/agents/agent.rb CHANGED
@@ -33,7 +33,7 @@
33
33
  # )
34
34
  module Agents
35
35
  class Agent
36
- attr_reader :name, :instructions, :model, :tools, :handoff_agents
36
+ attr_reader :name, :instructions, :model, :tools, :handoff_agents, :temperature
37
37
 
38
38
  # Initialize a new Agent instance
39
39
  #
@@ -42,12 +42,14 @@ module Agents
42
42
  # @param model [String] The LLM model to use (default: "gpt-4.1-mini")
43
43
  # @param tools [Array<Agents::Tool>] Array of tool instances the agent can use
44
44
  # @param handoff_agents [Array<Agents::Agent>] Array of agents this agent can hand off to
45
- def initialize(name:, instructions: nil, model: "gpt-4.1-mini", tools: [], handoff_agents: [])
45
+ # @param temperature [Float] Controls randomness in responses (0.0 = deterministic, 1.0 = very random, default: 0.7)
46
+ def initialize(name:, instructions: nil, model: "gpt-4.1-mini", tools: [], handoff_agents: [], temperature: 0.7)
46
47
  @name = name
47
48
  @instructions = instructions
48
49
  @model = model
49
50
  @tools = tools.dup
50
51
  @handoff_agents = []
52
+ @temperature = temperature
51
53
 
52
54
  # Mutex for thread-safe handoff registration
53
55
  # While agents are typically configured at startup, we want to ensure
@@ -131,6 +133,7 @@ module Agents
131
133
  # @option changes [String] :model New model identifier
132
134
  # @option changes [Array<Agents::Tool>] :tools New tools array (replaces all tools)
133
135
  # @option changes [Array<Agents::Agent>] :handoff_agents New handoff agents
136
+ # @option changes [Float] :temperature Temperature for LLM responses (0.0-1.0)
134
137
  # @return [Agents::Agent] A new frozen agent instance with the specified changes
135
138
  def clone(**changes)
136
139
  self.class.new(
@@ -138,7 +141,8 @@ module Agents
138
141
  instructions: changes.fetch(:instructions, @instructions),
139
142
  model: changes.fetch(:model, @model),
140
143
  tools: changes.fetch(:tools, @tools.dup),
141
- handoff_agents: changes.fetch(:handoff_agents, @handoff_agents)
144
+ handoff_agents: changes.fetch(:handoff_agents, @handoff_agents),
145
+ temperature: changes.fetch(:temperature, @temperature)
142
146
  )
143
147
  end
144
148
 
data/lib/agents/chat.rb CHANGED
@@ -26,11 +26,14 @@ module Agents
26
26
  end
27
27
  end
28
28
 
29
- def initialize(model: nil, handoff_tools: [], context_wrapper: nil, **options)
29
+ def initialize(model: nil, handoff_tools: [], context_wrapper: nil, temperature: nil, **options)
30
30
  super(model: model, **options)
31
31
  @handoff_tools = handoff_tools
32
32
  @context_wrapper = context_wrapper
33
33
 
34
+ # Set temperature if provided (RubyLLM::Chat sets this via accessor)
35
+ @temperature = temperature if temperature
36
+
34
37
  # Register handoff tools with RubyLLM for schema generation
35
38
  @handoff_tools.each { |tool| with_tool(tool) }
36
39
  end
data/lib/agents/runner.rb CHANGED
@@ -246,6 +246,7 @@ module Agents
246
246
  # Create extended chat with handoff awareness and context
247
247
  chat = Agents::Chat.new(
248
248
  model: agent.model,
249
+ temperature: agent.temperature,
249
250
  handoff_tools: handoff_tools, # Direct tools, no wrapper
250
251
  context_wrapper: context_wrapper # Pass context directly
251
252
  )
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Agents
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ai-agents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shivam Mishra