ai-agents 0.1.1 → 0.1.2

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: cc088132bc2b8dafa23669d0e2e15d110f6a8cb982f54ba026247008a90322ca
4
- data.tar.gz: 93086217d9aa75ebf072a299aeadacf688cede7216bd41d64dc539072ec1469b
3
+ metadata.gz: 004f33c8652e5b8c4d8be7bd84e959c55f6926411d3cac8bd3b3d029b7b21690
4
+ data.tar.gz: 6592c590cd91809ee47865a25adcf734370695bd4c697b1926aabc95c6e2d574
5
5
  SHA512:
6
- metadata.gz: 7ac77c92850152aa9bb716d3d1ecd37444e7cab76c9ab5e4e63aef20ad337a2416f9b4a1d55895ff0d0af97d4e8903c1e73f03c3e7a8f9bd6de0b8ed2f4279d6
7
- data.tar.gz: 4b3055a233c48902fdb570ca8dce896d09cdbd30dc52a0a5f03fb290c7f60725804d574e5df3e3d4428c61ad1af58b97d0f173c5b576a324ae84598f4d48fbbc
6
+ metadata.gz: 0baadd9d0771c8e2bdd345a0a8e00bc792c6a607ec78b8796a6718e4695dc5e92b2fdc2ec11b4384c77ad409af4660be5288b020c6404750a2db5a66d1cf094c
7
+ data.tar.gz: de6edbb743e8b01634aaa12c656bc7cfa618de8086aa4e4e6add08683e8383b611856b4b9fa452c2e2e2bbd3efa2a74edefcc17af8188d2605babfc5067ecaf2
@@ -51,7 +51,7 @@ module ISPSupport
51
51
  def create_sales_agent
52
52
  Agents::Agent.new(
53
53
  name: "Sales Agent",
54
- instructions: sales_instructions,
54
+ instructions: sales_instructions_with_state,
55
55
  model: "gpt-4.1-mini",
56
56
  tools: [ISPSupport::CreateLeadTool.new, ISPSupport::CreateCheckoutTool.new]
57
57
  )
@@ -113,6 +113,62 @@ module ISPSupport
113
113
  INSTRUCTIONS
114
114
  end
115
115
 
116
+ def sales_instructions_with_state
117
+ lambda { |context|
118
+ state = context.context[:state] || {}
119
+
120
+ base_instructions = <<~INSTRUCTIONS
121
+ You are the Sales Agent for an ISP. You handle new customer acquisition, service upgrades,
122
+ and plan changes.
123
+
124
+ **Your tools:**
125
+ - `create_lead`: Create sales leads with customer information
126
+ - `create_checkout`: Generate secure checkout links for purchases
127
+ - Handoff tools: Route back to triage when needed
128
+
129
+ **When to hand off:**
130
+ - Pure technical support questions → Triage Agent for re-routing
131
+ - Customer needs to speak with human agent → Triage Agent for re-routing
132
+ INSTRUCTIONS
133
+
134
+ # Add customer context if available from previous agent interactions
135
+ if state[:customer_name] && state[:customer_id]
136
+ base_instructions += <<~CONTEXT
137
+
138
+ **Customer Context Available:**
139
+ - Customer Name: #{state[:customer_name]}
140
+ - Customer ID: #{state[:customer_id]}
141
+ - Email: #{state[:customer_email]}
142
+ - Phone: #{state[:customer_phone]}
143
+ - Address: #{state[:customer_address]}
144
+ #{state[:current_plan] ? "- Current Plan: #{state[:current_plan]}" : ""}
145
+ #{state[:account_status] ? "- Account Status: #{state[:account_status]}" : ""}
146
+ #{state[:monthly_usage] ? "- Monthly Usage: #{state[:monthly_usage]}GB" : ""}
147
+
148
+ **IMPORTANT:**#{" "}
149
+ - Use this existing customer information when creating leads or providing service
150
+ - Do NOT ask for name, email, phone, or address - you already have these details
151
+ - For new connections, use the existing customer details and only ask for the desired plan
152
+ - Provide personalized recommendations based on their current information
153
+ CONTEXT
154
+ end
155
+
156
+ base_instructions += <<~FINAL_INSTRUCTIONS
157
+
158
+ **Instructions:**
159
+ - Be enthusiastic but not pushy
160
+ - Gather required info: name, email, desired plan for leads
161
+ - For account verification, ask customer for their account details directly
162
+ - For existing customers wanting upgrades, collect account info and proceed
163
+ - Create checkout links for confirmed purchases
164
+ - Always explain next steps after creating leads or checkout links
165
+ - Handle billing questions yourself - don't hand off for account verification
166
+ FINAL_INSTRUCTIONS
167
+
168
+ base_instructions
169
+ }
170
+ end
171
+
116
172
  def support_instructions
117
173
  <<~INSTRUCTIONS
118
174
  You are the Support Agent for an ISP. You handle technical support, troubleshooting,
@@ -8,8 +8,22 @@ module ISPSupport
8
8
  param :email, type: "string", desc: "Customer's email address"
9
9
  param :desired_plan, type: "string", desc: "Plan the customer is interested in"
10
10
 
11
- def perform(_tool_context, name:, email:, desired_plan:)
12
- "Lead created for #{name} (#{email}) interested in #{desired_plan} plan. Sales team will contact within 24 hours."
11
+ def perform(tool_context, name:, email:, desired_plan:)
12
+ # Store lead information in state for follow-up
13
+ tool_context.state[:lead_name] = name
14
+ tool_context.state[:lead_email] = email
15
+ tool_context.state[:desired_plan] = desired_plan
16
+ tool_context.state[:lead_created_at] = Time.now.iso8601
17
+
18
+ # Check if we have existing customer info from CRM lookup
19
+ if tool_context.state[:customer_id]
20
+ existing_customer = tool_context.state[:customer_name]
21
+ "Lead created for existing customer #{existing_customer} (#{email}) " \
22
+ "interested in upgrading to #{desired_plan} plan. Sales team will contact within 24 hours."
23
+ else
24
+ "Lead created for #{name} (#{email}) interested in #{desired_plan} plan. " \
25
+ "Sales team will contact within 24 hours."
26
+ end
13
27
  end
14
28
  end
15
29
  end
@@ -8,7 +8,7 @@ module ISPSupport
8
8
  description "Look up customer account information by account ID"
9
9
  param :account_id, type: "string", desc: "Customer account ID (e.g., CUST001)"
10
10
 
11
- def perform(_tool_context, account_id:)
11
+ def perform(tool_context, account_id:)
12
12
  data_file = File.join(__dir__, "../data/customers.json")
13
13
  return "Customer database unavailable" unless File.exist?(data_file)
14
14
 
@@ -18,6 +18,18 @@ module ISPSupport
18
18
 
19
19
  return "Customer not found" unless customer
20
20
 
21
+ # Store customer information in shared state for other tools/agents
22
+ tool_context.state[:customer_id] = account_id.upcase
23
+ tool_context.state[:customer_name] = customer["name"]
24
+ tool_context.state[:customer_email] = customer["email"]
25
+ tool_context.state[:customer_phone] = customer["phone"]
26
+ tool_context.state[:customer_address] = customer["address"]
27
+ tool_context.state[:current_plan] = customer["plan"]["name"]
28
+ tool_context.state[:account_status] = customer["account_status"]
29
+ tool_context.state[:plan_price] = customer["plan"]["price"]
30
+ tool_context.state[:next_bill_date] = customer["billing"]["next_bill_date"]
31
+ tool_context.state[:account_balance] = customer["billing"]["balance"]
32
+
21
33
  # Return the entire customer data as JSON for the agent to process
22
34
  customer.to_json
23
35
  rescue StandardError
data/lib/agents/agent.rb CHANGED
@@ -13,11 +13,16 @@
13
13
  # tools: [calculator_tool, weather_tool]
14
14
  # )
15
15
  #
16
- # @example Creating an agent with dynamic instructions
16
+ # @example Creating an agent with dynamic state-aware instructions
17
17
  # agent = Agents::Agent.new(
18
18
  # name: "Support Agent",
19
19
  # instructions: ->(context) {
20
- # "You are supporting user #{context.context[:user_name]}"
20
+ # state = context.context[:state] || {}
21
+ # base = "You are a support agent."
22
+ # if state[:customer_name]
23
+ # base += " Customer: #{state[:customer_name]} (#{state[:customer_id]})"
24
+ # end
25
+ # base
21
26
  # }
22
27
  # )
23
28
  #
@@ -147,18 +152,25 @@ module Agents
147
152
  # instructions: "You are a helpful support agent"
148
153
  # )
149
154
  #
150
- # @example Dynamic instructions based on context
155
+ # @example Dynamic instructions with state awareness
151
156
  # agent = Agent.new(
152
- # name: "Support",
157
+ # name: "Sales Agent",
153
158
  # instructions: ->(context) {
154
- # user = context.context[:user]
155
- # "You are helping #{user.name}. They are a #{user.tier} customer with account #{user.id}"
159
+ # state = context.context[:state] || {}
160
+ # base = "You are a sales agent."
161
+ # if state[:customer_name] && state[:current_plan]
162
+ # base += " Customer: #{state[:customer_name]} on #{state[:current_plan]} plan."
163
+ # end
164
+ # base
156
165
  # }
157
166
  # )
158
167
  #
159
168
  # @param context [Agents::RunContext] The current execution context containing runtime data
160
169
  # @return [String, nil] The system prompt string or nil if no instructions are set
161
170
  def get_system_prompt(context)
171
+ # TODO: Add string interpolation support for instructions
172
+ # Allow instructions like "You are helping %{customer_name}" that automatically
173
+ # get state values injected from context[:state] using Ruby's % formatting
162
174
  case instructions
163
175
  when String
164
176
  instructions
@@ -94,5 +94,41 @@ module Agents
94
94
  def usage
95
95
  @run_context.usage
96
96
  end
97
+
98
+ # Convenient access to the shared state hash within the context.
99
+ # This provides tools with a dedicated space to store and retrieve
100
+ # state that persists across agent interactions within a conversation.
101
+ #
102
+ # State is automatically initialized as an empty hash if it doesn't exist.
103
+ # All state modifications are automatically included in context serialization,
104
+ # making it persist across process boundaries (e.g., Rails with ActiveRecord).
105
+ #
106
+ # @return [Hash] The shared state hash
107
+ # @example Tool storing customer information in state
108
+ # def perform(tool_context, customer_id:)
109
+ # customer = Customer.find(customer_id)
110
+ #
111
+ # # Store in shared state for other tools/agents to access
112
+ # tool_context.state[:customer_id] = customer_id
113
+ # tool_context.state[:customer_name] = customer.name
114
+ # tool_context.state[:plan_type] = customer.plan
115
+ #
116
+ # "Found customer #{customer.name}"
117
+ # end
118
+ #
119
+ # @example Tool reading from shared state
120
+ # def perform(tool_context)
121
+ # customer_id = tool_context.state[:customer_id]
122
+ # plan_type = tool_context.state[:plan_type]
123
+ #
124
+ # if customer_id && plan_type
125
+ # "Current plan: #{plan_type} for customer #{customer_id}"
126
+ # else
127
+ # "No customer information available"
128
+ # end
129
+ # end
130
+ def state
131
+ context[:state] ||= {}
132
+ end
97
133
  end
98
134
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Agents
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
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.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shivam Mishra
@@ -75,7 +75,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
75
  requirements:
76
76
  - - ">="
77
77
  - !ruby/object:Gem::Version
78
- version: 3.1.0
78
+ version: 3.2.0
79
79
  required_rubygems_version: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - ">="