simple_acp 0.0.1
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 +7 -0
- data/.envrc +1 -0
- data/CHANGELOG.md +5 -0
- data/COMMITS.md +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +385 -0
- data/Rakefile +13 -0
- data/docs/api/client-base.md +383 -0
- data/docs/api/index.md +159 -0
- data/docs/api/models.md +286 -0
- data/docs/api/server-base.md +379 -0
- data/docs/api/storage.md +347 -0
- data/docs/assets/images/simple_acp.jpg +0 -0
- data/docs/client/index.md +279 -0
- data/docs/client/sessions.md +324 -0
- data/docs/client/streaming.md +345 -0
- data/docs/client/sync-async.md +308 -0
- data/docs/core-concepts/agents.md +253 -0
- data/docs/core-concepts/events.md +337 -0
- data/docs/core-concepts/index.md +147 -0
- data/docs/core-concepts/messages.md +211 -0
- data/docs/core-concepts/runs.md +278 -0
- data/docs/core-concepts/sessions.md +281 -0
- data/docs/examples.md +659 -0
- data/docs/getting-started/configuration.md +166 -0
- data/docs/getting-started/index.md +62 -0
- data/docs/getting-started/installation.md +95 -0
- data/docs/getting-started/quick-start.md +189 -0
- data/docs/index.md +119 -0
- data/docs/server/creating-agents.md +360 -0
- data/docs/server/http-endpoints.md +411 -0
- data/docs/server/index.md +218 -0
- data/docs/server/multi-turn.md +329 -0
- data/docs/server/streaming.md +315 -0
- data/docs/storage/custom.md +414 -0
- data/docs/storage/index.md +176 -0
- data/docs/storage/memory.md +198 -0
- data/docs/storage/postgresql.md +350 -0
- data/docs/storage/redis.md +287 -0
- data/examples/01_basic/client.rb +88 -0
- data/examples/01_basic/server.rb +100 -0
- data/examples/02_async_execution/client.rb +107 -0
- data/examples/02_async_execution/server.rb +56 -0
- data/examples/03_run_management/client.rb +115 -0
- data/examples/03_run_management/server.rb +84 -0
- data/examples/04_rich_messages/client.rb +160 -0
- data/examples/04_rich_messages/server.rb +180 -0
- data/examples/05_await_resume/client.rb +164 -0
- data/examples/05_await_resume/server.rb +114 -0
- data/examples/06_agent_metadata/client.rb +188 -0
- data/examples/06_agent_metadata/server.rb +192 -0
- data/examples/README.md +252 -0
- data/examples/run_demo.sh +137 -0
- data/lib/simple_acp/client/base.rb +448 -0
- data/lib/simple_acp/client/sse.rb +141 -0
- data/lib/simple_acp/models/agent_manifest.rb +129 -0
- data/lib/simple_acp/models/await.rb +123 -0
- data/lib/simple_acp/models/base.rb +147 -0
- data/lib/simple_acp/models/errors.rb +102 -0
- data/lib/simple_acp/models/events.rb +256 -0
- data/lib/simple_acp/models/message.rb +235 -0
- data/lib/simple_acp/models/message_part.rb +225 -0
- data/lib/simple_acp/models/metadata.rb +161 -0
- data/lib/simple_acp/models/run.rb +298 -0
- data/lib/simple_acp/models/session.rb +137 -0
- data/lib/simple_acp/models/types.rb +210 -0
- data/lib/simple_acp/server/agent.rb +116 -0
- data/lib/simple_acp/server/app.rb +264 -0
- data/lib/simple_acp/server/base.rb +510 -0
- data/lib/simple_acp/server/context.rb +210 -0
- data/lib/simple_acp/server/falcon_runner.rb +61 -0
- data/lib/simple_acp/storage/base.rb +129 -0
- data/lib/simple_acp/storage/memory.rb +108 -0
- data/lib/simple_acp/storage/postgresql.rb +233 -0
- data/lib/simple_acp/storage/redis.rb +178 -0
- data/lib/simple_acp/version.rb +5 -0
- data/lib/simple_acp.rb +91 -0
- data/mkdocs.yml +152 -0
- data/sig/simple_acp.rbs +4 -0
- metadata +418 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Await/Resume Example - Server
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates the await/resume pattern for interactive multi-step flows.
|
|
7
|
+
# The agent can pause execution and request input from the client.
|
|
8
|
+
#
|
|
9
|
+
# Run with: ruby examples/05_await_resume/server.rb
|
|
10
|
+
# Then connect with: ruby examples/05_await_resume/client.rb
|
|
11
|
+
|
|
12
|
+
require_relative "../../lib/simple_acp"
|
|
13
|
+
|
|
14
|
+
server = SimpleAcp::Server::Base.new
|
|
15
|
+
|
|
16
|
+
# Simple single-question agent
|
|
17
|
+
server.agent("greeter",
|
|
18
|
+
description: "Asks for your name and greets you"
|
|
19
|
+
) do |context|
|
|
20
|
+
if context.resume_message
|
|
21
|
+
# Resume: we have the client's response
|
|
22
|
+
name = context.resume_message.text_content
|
|
23
|
+
SimpleAcp::Models::Message.agent("Hello, #{name}! Nice to meet you!")
|
|
24
|
+
else
|
|
25
|
+
# Initial call: ask for input
|
|
26
|
+
context.await_message(
|
|
27
|
+
SimpleAcp::Models::Message.agent("What is your name?")
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Multi-step wizard using state to track progress
|
|
33
|
+
server.agent("survey",
|
|
34
|
+
description: "A multi-step survey that collects information"
|
|
35
|
+
) do |context|
|
|
36
|
+
# Use state to track current step and collected data
|
|
37
|
+
survey_state = context.state || { step: 0, answers: {} }
|
|
38
|
+
step = survey_state[:step]
|
|
39
|
+
|
|
40
|
+
case step
|
|
41
|
+
when 0
|
|
42
|
+
# Step 1: Ask for name
|
|
43
|
+
context.set_state({ step: 1, answers: {} })
|
|
44
|
+
context.await_message(
|
|
45
|
+
SimpleAcp::Models::Message.agent("Welcome to the survey! Question 1: What is your name?")
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
when 1
|
|
49
|
+
# Step 2: Got name, ask for favorite color
|
|
50
|
+
answers = survey_state[:answers].merge("name" => context.resume_message&.text_content)
|
|
51
|
+
context.set_state({ step: 2, answers: answers })
|
|
52
|
+
context.await_message(
|
|
53
|
+
SimpleAcp::Models::Message.agent("Thanks #{answers['name']}! Question 2: What is your favorite color?")
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
when 2
|
|
57
|
+
# Step 3: Got color, ask for favorite number
|
|
58
|
+
answers = survey_state[:answers].merge("color" => context.resume_message&.text_content)
|
|
59
|
+
context.set_state({ step: 3, answers: answers })
|
|
60
|
+
context.await_message(
|
|
61
|
+
SimpleAcp::Models::Message.agent("Great choice! Question 3: What is your favorite number?")
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
when 3
|
|
65
|
+
# Final step: Got all answers, return summary
|
|
66
|
+
answers = survey_state[:answers].merge("number" => context.resume_message&.text_content)
|
|
67
|
+
context.set_state({ step: 0, answers: {} }) # Reset for next survey
|
|
68
|
+
|
|
69
|
+
summary = <<~SUMMARY
|
|
70
|
+
Survey complete! Here are your answers:
|
|
71
|
+
- Name: #{answers['name']}
|
|
72
|
+
- Favorite color: #{answers['color']}
|
|
73
|
+
- Favorite number: #{answers['number']}
|
|
74
|
+
Thank you for participating!
|
|
75
|
+
SUMMARY
|
|
76
|
+
|
|
77
|
+
SimpleAcp::Models::Message.agent(summary)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Confirmation agent - asks for yes/no confirmation
|
|
82
|
+
server.agent("confirmer",
|
|
83
|
+
description: "Performs an action after confirmation"
|
|
84
|
+
) do |context|
|
|
85
|
+
action = context.input.first&.text_content || "do something"
|
|
86
|
+
state = context.state || { confirmed: nil }
|
|
87
|
+
|
|
88
|
+
if state[:confirmed].nil?
|
|
89
|
+
# First call: ask for confirmation
|
|
90
|
+
context.set_state({ action: action, confirmed: false })
|
|
91
|
+
context.await_message(
|
|
92
|
+
SimpleAcp::Models::Message.agent("Are you sure you want to '#{action}'? (yes/no)")
|
|
93
|
+
)
|
|
94
|
+
else
|
|
95
|
+
# Resume: check confirmation
|
|
96
|
+
response = context.resume_message&.text_content&.downcase&.strip
|
|
97
|
+
context.set_state(nil) # Clear state
|
|
98
|
+
|
|
99
|
+
if %w[yes y sure ok].include?(response)
|
|
100
|
+
SimpleAcp::Models::Message.agent("Action '#{state[:action]}' confirmed and executed!")
|
|
101
|
+
else
|
|
102
|
+
SimpleAcp::Models::Message.agent("Action '#{state[:action]}' cancelled.")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
puts "Starting Await/Resume Demo Server..."
|
|
108
|
+
puts "Available agents:"
|
|
109
|
+
server.agents.each do |name, agent|
|
|
110
|
+
puts " - #{name}: #{agent.description}"
|
|
111
|
+
end
|
|
112
|
+
puts
|
|
113
|
+
|
|
114
|
+
server.run(port: 8000)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Agent Metadata Example - Client
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates:
|
|
7
|
+
# - Retrieving full agent metadata
|
|
8
|
+
# - Inspecting capabilities, author, links
|
|
9
|
+
# - Content type negotiation
|
|
10
|
+
# - Using the agent(name) endpoint
|
|
11
|
+
#
|
|
12
|
+
# First start the server: ruby examples/06_agent_metadata/server.rb
|
|
13
|
+
# Then run this client: ruby examples/06_agent_metadata/client.rb
|
|
14
|
+
|
|
15
|
+
require_relative "../../lib/simple_acp"
|
|
16
|
+
require "json"
|
|
17
|
+
|
|
18
|
+
client = SimpleAcp::Client::Base.new(base_url: "http://localhost:8000")
|
|
19
|
+
|
|
20
|
+
puts "=== Agent Metadata Demo ==="
|
|
21
|
+
puts
|
|
22
|
+
|
|
23
|
+
# Verify server is running
|
|
24
|
+
unless client.ping
|
|
25
|
+
puts "Server is not responding"
|
|
26
|
+
exit 1
|
|
27
|
+
end
|
|
28
|
+
puts "Server is healthy"
|
|
29
|
+
puts
|
|
30
|
+
|
|
31
|
+
# --- Demo 1: List all agents with basic info ---
|
|
32
|
+
puts "--- Demo 1: List Agents Overview ---"
|
|
33
|
+
puts
|
|
34
|
+
|
|
35
|
+
agents_response = client.agents
|
|
36
|
+
puts "Found #{agents_response.agents.length} agents:"
|
|
37
|
+
puts
|
|
38
|
+
agents_response.agents.each do |agent|
|
|
39
|
+
puts "#{agent.name}"
|
|
40
|
+
puts " Description: #{agent.description}"
|
|
41
|
+
puts " Input types: #{agent.input_content_types.join(', ')}"
|
|
42
|
+
puts " Output types: #{agent.output_content_types.join(', ')}"
|
|
43
|
+
puts
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# --- Demo 2: Get detailed agent metadata ---
|
|
47
|
+
puts "--- Demo 2: Detailed Agent Metadata ---"
|
|
48
|
+
puts
|
|
49
|
+
|
|
50
|
+
agent = client.agent("text-analyzer")
|
|
51
|
+
|
|
52
|
+
puts "Agent: #{agent.name}"
|
|
53
|
+
puts "Description: #{agent.description}"
|
|
54
|
+
puts
|
|
55
|
+
|
|
56
|
+
if agent.metadata
|
|
57
|
+
meta = agent.metadata
|
|
58
|
+
|
|
59
|
+
puts "Documentation:"
|
|
60
|
+
puts " #{meta.documentation}"
|
|
61
|
+
puts
|
|
62
|
+
|
|
63
|
+
if meta.author
|
|
64
|
+
puts "Author:"
|
|
65
|
+
puts " Name: #{meta.author.name}"
|
|
66
|
+
puts " Email: #{meta.author.email}" if meta.author.email
|
|
67
|
+
puts " URL: #{meta.author.url}" if meta.author.url
|
|
68
|
+
puts
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if meta.contributors&.any?
|
|
72
|
+
puts "Contributors:"
|
|
73
|
+
meta.contributors.each do |contrib|
|
|
74
|
+
puts " - #{contrib.name}"
|
|
75
|
+
puts " Email: #{contrib.email}" if contrib.email
|
|
76
|
+
puts " URL: #{contrib.url}" if contrib.url
|
|
77
|
+
end
|
|
78
|
+
puts
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if meta.capabilities&.any?
|
|
82
|
+
puts "Capabilities:"
|
|
83
|
+
meta.capabilities.each do |cap|
|
|
84
|
+
puts " - #{cap.name}: #{cap.description}"
|
|
85
|
+
end
|
|
86
|
+
puts
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if meta.links&.any?
|
|
90
|
+
puts "Links:"
|
|
91
|
+
meta.links.each do |link|
|
|
92
|
+
puts " - #{link.type}: #{link.url}"
|
|
93
|
+
end
|
|
94
|
+
puts
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
if meta.dependencies&.any?
|
|
98
|
+
puts "Dependencies:"
|
|
99
|
+
meta.dependencies.each do |dep|
|
|
100
|
+
puts " - #{dep.type}: #{dep.name}"
|
|
101
|
+
end
|
|
102
|
+
puts
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
puts "Additional Info:"
|
|
106
|
+
puts " License: #{meta.license}" if meta.license
|
|
107
|
+
puts " Language: #{meta.programming_language}" if meta.programming_language
|
|
108
|
+
puts " Framework: #{meta.framework}" if meta.framework
|
|
109
|
+
puts " Natural languages: #{meta.natural_languages.join(', ')}" if meta.natural_languages&.any?
|
|
110
|
+
puts " Domains: #{meta.domains.join(', ')}" if meta.domains&.any?
|
|
111
|
+
puts " Tags: #{meta.tags.join(', ')}" if meta.tags&.any?
|
|
112
|
+
puts " Created: #{meta.created_at}" if meta.created_at
|
|
113
|
+
puts " Updated: #{meta.updated_at}" if meta.updated_at
|
|
114
|
+
puts " Recommended models: #{meta.recommended_models.join(', ')}" if meta.recommended_models&.any?
|
|
115
|
+
else
|
|
116
|
+
puts "(No detailed metadata)"
|
|
117
|
+
end
|
|
118
|
+
puts
|
|
119
|
+
|
|
120
|
+
# --- Demo 3: Content Type Negotiation ---
|
|
121
|
+
puts "--- Demo 3: Content Type Checking ---"
|
|
122
|
+
puts
|
|
123
|
+
|
|
124
|
+
agent = client.agent("text-analyzer")
|
|
125
|
+
|
|
126
|
+
test_types = ["text/plain", "text/markdown", "application/json", "image/png", "text/html"]
|
|
127
|
+
|
|
128
|
+
puts "Testing input content types for #{agent.name}:"
|
|
129
|
+
test_types.each do |type|
|
|
130
|
+
accepts = agent.accepts_content_type?(type)
|
|
131
|
+
puts " #{type}: #{accepts ? 'accepted' : 'rejected'}"
|
|
132
|
+
end
|
|
133
|
+
puts
|
|
134
|
+
|
|
135
|
+
puts "Testing output content types for #{agent.name}:"
|
|
136
|
+
test_types.each do |type|
|
|
137
|
+
produces = agent.produces_content_type?(type)
|
|
138
|
+
puts " #{type}: #{produces ? 'produced' : 'not produced'}"
|
|
139
|
+
end
|
|
140
|
+
puts
|
|
141
|
+
|
|
142
|
+
# --- Demo 4: Compare agents with different metadata levels ---
|
|
143
|
+
puts "--- Demo 4: Metadata Comparison ---"
|
|
144
|
+
puts
|
|
145
|
+
|
|
146
|
+
%w[text-analyzer simple-echo json-processor].each do |name|
|
|
147
|
+
agent = client.agent(name)
|
|
148
|
+
puts "#{name}:"
|
|
149
|
+
puts " Has metadata: #{agent.metadata ? 'yes' : 'no'}"
|
|
150
|
+
if agent.metadata
|
|
151
|
+
puts " Domains: #{agent.metadata.domains&.join(', ') || 'none'}"
|
|
152
|
+
puts " Tags: #{agent.metadata.tags&.join(', ') || 'none'}"
|
|
153
|
+
puts " Capabilities: #{agent.metadata.capabilities&.length || 0}"
|
|
154
|
+
puts " Links: #{agent.metadata.links&.length || 0}"
|
|
155
|
+
end
|
|
156
|
+
puts
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# --- Demo 5: Using the text analyzer ---
|
|
160
|
+
puts "--- Demo 5: Using Text Analyzer Agent ---"
|
|
161
|
+
puts
|
|
162
|
+
|
|
163
|
+
sample_text = "Ruby is a wonderful programming language. It has great syntax and an amazing community. I love writing code in Ruby because it makes me happy and productive."
|
|
164
|
+
|
|
165
|
+
puts "Analyzing text:"
|
|
166
|
+
puts " \"#{sample_text[0..60]}...\""
|
|
167
|
+
puts
|
|
168
|
+
|
|
169
|
+
run = client.run_sync(agent: "text-analyzer", input: sample_text)
|
|
170
|
+
|
|
171
|
+
run.output.each do |message|
|
|
172
|
+
message.parts.each do |part|
|
|
173
|
+
case part.content_type
|
|
174
|
+
when "application/json"
|
|
175
|
+
data = JSON.parse(part.content)
|
|
176
|
+
puts "Results:"
|
|
177
|
+
puts " Word count: #{data['word_count']}"
|
|
178
|
+
puts " Sentiment: #{data['sentiment']}"
|
|
179
|
+
puts " Keywords: #{data['keywords'].join(', ')}"
|
|
180
|
+
puts " Summary: #{data['summary']}"
|
|
181
|
+
when "text/plain"
|
|
182
|
+
puts " #{part.content}"
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
puts
|
|
187
|
+
|
|
188
|
+
puts "=== Agent Metadata Demo Complete ==="
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Agent Metadata Example - Server
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates rich agent metadata including:
|
|
7
|
+
# - Author and contributor information
|
|
8
|
+
# - Links to documentation and source code
|
|
9
|
+
# - Capabilities and domains
|
|
10
|
+
# - Dependencies
|
|
11
|
+
# - Content type negotiation
|
|
12
|
+
#
|
|
13
|
+
# Run with: ruby examples/06_agent_metadata/server.rb
|
|
14
|
+
# Then connect with: ruby examples/06_agent_metadata/client.rb
|
|
15
|
+
|
|
16
|
+
require_relative "../../lib/simple_acp"
|
|
17
|
+
|
|
18
|
+
server = SimpleAcp::Server::Base.new
|
|
19
|
+
|
|
20
|
+
# Agent with full metadata
|
|
21
|
+
metadata = SimpleAcp::Models::Metadata.new(
|
|
22
|
+
documentation: "A comprehensive text analysis agent that can summarize, analyze sentiment, and extract keywords from text.",
|
|
23
|
+
license: "MIT",
|
|
24
|
+
programming_language: "ruby",
|
|
25
|
+
natural_languages: ["en", "es", "fr"],
|
|
26
|
+
framework: "SimpleAcp",
|
|
27
|
+
created_at: "2024-01-15T10:00:00Z",
|
|
28
|
+
updated_at: "2024-06-20T14:30:00Z",
|
|
29
|
+
author: SimpleAcp::Models::Author.new(
|
|
30
|
+
name: "Alice Developer",
|
|
31
|
+
email: "alice@example.com",
|
|
32
|
+
url: "https://github.com/alicedev"
|
|
33
|
+
),
|
|
34
|
+
contributors: [
|
|
35
|
+
SimpleAcp::Models::Contributor.new(
|
|
36
|
+
name: "Bob Contributor",
|
|
37
|
+
email: "bob@example.com"
|
|
38
|
+
),
|
|
39
|
+
SimpleAcp::Models::Contributor.new(
|
|
40
|
+
name: "Charlie Helper",
|
|
41
|
+
url: "https://github.com/charliehelper"
|
|
42
|
+
)
|
|
43
|
+
],
|
|
44
|
+
capabilities: [
|
|
45
|
+
SimpleAcp::Models::Capability.new(
|
|
46
|
+
name: "summarize",
|
|
47
|
+
description: "Summarize long text into key points"
|
|
48
|
+
),
|
|
49
|
+
SimpleAcp::Models::Capability.new(
|
|
50
|
+
name: "sentiment",
|
|
51
|
+
description: "Analyze the sentiment (positive/negative/neutral)"
|
|
52
|
+
),
|
|
53
|
+
SimpleAcp::Models::Capability.new(
|
|
54
|
+
name: "keywords",
|
|
55
|
+
description: "Extract important keywords and phrases"
|
|
56
|
+
)
|
|
57
|
+
],
|
|
58
|
+
domains: ["nlp", "text-analysis", "ai"],
|
|
59
|
+
tags: ["text", "analysis", "nlp", "summarization", "sentiment"],
|
|
60
|
+
links: [
|
|
61
|
+
SimpleAcp::Models::Link.new(
|
|
62
|
+
type: "documentation",
|
|
63
|
+
url: "https://example.com/docs/text-analyzer"
|
|
64
|
+
),
|
|
65
|
+
SimpleAcp::Models::Link.new(
|
|
66
|
+
type: "source-code",
|
|
67
|
+
url: "https://github.com/example/text-analyzer"
|
|
68
|
+
),
|
|
69
|
+
SimpleAcp::Models::Link.new(
|
|
70
|
+
type: "homepage",
|
|
71
|
+
url: "https://example.com/text-analyzer"
|
|
72
|
+
)
|
|
73
|
+
],
|
|
74
|
+
dependencies: [
|
|
75
|
+
SimpleAcp::Models::Dependency.new(type: "agent", name: "tokenizer"),
|
|
76
|
+
SimpleAcp::Models::Dependency.new(type: "model", name: "gpt-4")
|
|
77
|
+
],
|
|
78
|
+
recommended_models: ["gpt-4", "claude-3", "llama-2"]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
server.agent("text-analyzer",
|
|
82
|
+
description: "Analyzes text for sentiment, keywords, and provides summaries",
|
|
83
|
+
input_content_types: ["text/plain", "text/markdown", "application/json"],
|
|
84
|
+
output_content_types: ["application/json", "text/plain"],
|
|
85
|
+
metadata: metadata
|
|
86
|
+
) do |context|
|
|
87
|
+
text = context.input.first&.text_content || ""
|
|
88
|
+
operation = "full"
|
|
89
|
+
|
|
90
|
+
# Parse operation from JSON if provided
|
|
91
|
+
if context.input.first&.parts&.first&.content_type == "application/json"
|
|
92
|
+
begin
|
|
93
|
+
data = JSON.parse(context.input.first.parts.first.content)
|
|
94
|
+
text = data["text"] || text
|
|
95
|
+
operation = data["operation"] || "full"
|
|
96
|
+
rescue JSON::ParserError
|
|
97
|
+
# Use text as-is
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Simulate text analysis
|
|
102
|
+
words = text.split(/\s+/)
|
|
103
|
+
word_count = words.length
|
|
104
|
+
|
|
105
|
+
# Simple keyword extraction (just take unique words > 4 chars)
|
|
106
|
+
keywords = words.map(&:downcase).uniq.select { |w| w.length > 4 }.first(5)
|
|
107
|
+
|
|
108
|
+
# Simple sentiment (just check for positive/negative words)
|
|
109
|
+
positive_words = %w[good great excellent amazing wonderful happy love best fantastic]
|
|
110
|
+
negative_words = %w[bad terrible awful horrible sad hate worst disappointing]
|
|
111
|
+
|
|
112
|
+
positive_count = words.count { |w| positive_words.include?(w.downcase) }
|
|
113
|
+
negative_count = words.count { |w| negative_words.include?(w.downcase) }
|
|
114
|
+
|
|
115
|
+
sentiment = if positive_count > negative_count
|
|
116
|
+
"positive"
|
|
117
|
+
elsif negative_count > positive_count
|
|
118
|
+
"negative"
|
|
119
|
+
else
|
|
120
|
+
"neutral"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
result = {
|
|
124
|
+
text_length: text.length,
|
|
125
|
+
word_count: word_count,
|
|
126
|
+
keywords: keywords,
|
|
127
|
+
sentiment: sentiment,
|
|
128
|
+
summary: words.first(20).join(" ") + (words.length > 20 ? "..." : "")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
SimpleAcp::Models::Message.new(
|
|
132
|
+
role: "agent",
|
|
133
|
+
parts: [
|
|
134
|
+
SimpleAcp::Models::MessagePart.json(result),
|
|
135
|
+
SimpleAcp::Models::MessagePart.text(
|
|
136
|
+
"Analysis complete: #{word_count} words, sentiment: #{sentiment}"
|
|
137
|
+
)
|
|
138
|
+
]
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Minimal agent for comparison
|
|
143
|
+
server.agent("simple-echo",
|
|
144
|
+
description: "A simple echo agent with minimal metadata"
|
|
145
|
+
) do |context|
|
|
146
|
+
input = context.input.first&.text_content || ""
|
|
147
|
+
SimpleAcp::Models::Message.agent("Echo: #{input}")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Agent with specific content types
|
|
151
|
+
server.agent("json-processor",
|
|
152
|
+
description: "Processes JSON input and returns JSON output",
|
|
153
|
+
input_content_types: ["application/json"],
|
|
154
|
+
output_content_types: ["application/json"],
|
|
155
|
+
metadata: SimpleAcp::Models::Metadata.new(
|
|
156
|
+
documentation: "A JSON processor that transforms input data",
|
|
157
|
+
domains: ["data-processing"],
|
|
158
|
+
tags: ["json", "transform"]
|
|
159
|
+
)
|
|
160
|
+
) do |context|
|
|
161
|
+
begin
|
|
162
|
+
input_data = JSON.parse(context.input.first&.text_content || "{}")
|
|
163
|
+
output = {
|
|
164
|
+
received: input_data,
|
|
165
|
+
processed_at: Time.now.iso8601,
|
|
166
|
+
keys: input_data.keys
|
|
167
|
+
}
|
|
168
|
+
SimpleAcp::Models::Message.new(
|
|
169
|
+
role: "agent",
|
|
170
|
+
parts: [SimpleAcp::Models::MessagePart.json(output)]
|
|
171
|
+
)
|
|
172
|
+
rescue JSON::ParserError => e
|
|
173
|
+
SimpleAcp::Models::Message.new(
|
|
174
|
+
role: "agent",
|
|
175
|
+
parts: [SimpleAcp::Models::MessagePart.json({ error: e.message })]
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
puts "Starting Agent Metadata Demo Server..."
|
|
181
|
+
puts "Available agents:"
|
|
182
|
+
server.agents.each do |name, agent|
|
|
183
|
+
puts " - #{name}: #{agent.description}"
|
|
184
|
+
puts " Input types: #{agent.manifest.input_content_types.join(', ')}"
|
|
185
|
+
puts " Output types: #{agent.manifest.output_content_types.join(', ')}"
|
|
186
|
+
if agent.manifest.metadata
|
|
187
|
+
puts " Has metadata: author, capabilities, links, etc."
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
puts
|
|
191
|
+
|
|
192
|
+
server.run(port: 8000)
|