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,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAcp
|
|
4
|
+
module Models
|
|
5
|
+
# Base class for await requests.
|
|
6
|
+
#
|
|
7
|
+
# When an agent needs client input during execution, it creates
|
|
8
|
+
# an await request describing what input is needed.
|
|
9
|
+
#
|
|
10
|
+
# @abstract Subclass for specific await types
|
|
11
|
+
class AwaitRequest < Base
|
|
12
|
+
# @!attribute [r] type
|
|
13
|
+
# @return [String] await request type
|
|
14
|
+
attribute :type, required: true
|
|
15
|
+
|
|
16
|
+
# Parse from hash, creating the appropriate subclass.
|
|
17
|
+
#
|
|
18
|
+
# @param hash [Hash, nil] await request data
|
|
19
|
+
# @return [AwaitRequest, nil] the request or nil
|
|
20
|
+
def self.from_hash(hash)
|
|
21
|
+
return nil if hash.nil?
|
|
22
|
+
|
|
23
|
+
type = hash["type"] || hash[:type]
|
|
24
|
+
|
|
25
|
+
case type
|
|
26
|
+
when "message"
|
|
27
|
+
MessageAwaitRequest.from_hash(hash)
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Request for a message from the client.
|
|
35
|
+
#
|
|
36
|
+
# Used when an agent needs text or other message input to continue.
|
|
37
|
+
class MessageAwaitRequest < AwaitRequest
|
|
38
|
+
# @!attribute [r] message
|
|
39
|
+
# @return [Message, nil] prompt message to display to client
|
|
40
|
+
attribute :message
|
|
41
|
+
|
|
42
|
+
def initialize(**kwargs)
|
|
43
|
+
kwargs[:type] = "message"
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.from_hash(hash)
|
|
48
|
+
return nil if hash.nil?
|
|
49
|
+
|
|
50
|
+
instance = allocate
|
|
51
|
+
instance.instance_variable_set(:@type, "message")
|
|
52
|
+
|
|
53
|
+
if hash["message"] || hash[:message]
|
|
54
|
+
instance.instance_variable_set(
|
|
55
|
+
:@message,
|
|
56
|
+
Message.from_hash(hash["message"] || hash[:message])
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
instance
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Base class for await resume payloads.
|
|
65
|
+
#
|
|
66
|
+
# Clients send resume payloads to continue an awaited run
|
|
67
|
+
# with the requested input.
|
|
68
|
+
#
|
|
69
|
+
# @abstract Subclass for specific resume types
|
|
70
|
+
class AwaitResume < Base
|
|
71
|
+
# @!attribute [r] type
|
|
72
|
+
# @return [String] resume type
|
|
73
|
+
attribute :type, required: true
|
|
74
|
+
|
|
75
|
+
# Parse from hash, creating the appropriate subclass.
|
|
76
|
+
#
|
|
77
|
+
# @param hash [Hash, nil] resume payload data
|
|
78
|
+
# @return [AwaitResume, nil] the resume or nil
|
|
79
|
+
def self.from_hash(hash)
|
|
80
|
+
return nil if hash.nil?
|
|
81
|
+
|
|
82
|
+
type = hash["type"] || hash[:type]
|
|
83
|
+
|
|
84
|
+
case type
|
|
85
|
+
when "message"
|
|
86
|
+
MessageAwaitResume.from_hash(hash)
|
|
87
|
+
else
|
|
88
|
+
super
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Resume payload containing a message response.
|
|
94
|
+
#
|
|
95
|
+
# Used to provide text or other message input to an awaiting run.
|
|
96
|
+
class MessageAwaitResume < AwaitResume
|
|
97
|
+
# @!attribute [r] message
|
|
98
|
+
# @return [Message, nil] the client's response message
|
|
99
|
+
attribute :message
|
|
100
|
+
|
|
101
|
+
def initialize(**kwargs)
|
|
102
|
+
kwargs[:type] = "message"
|
|
103
|
+
super
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.from_hash(hash)
|
|
107
|
+
return nil if hash.nil?
|
|
108
|
+
|
|
109
|
+
instance = allocate
|
|
110
|
+
instance.instance_variable_set(:@type, "message")
|
|
111
|
+
|
|
112
|
+
if hash["message"] || hash[:message]
|
|
113
|
+
instance.instance_variable_set(
|
|
114
|
+
:@message,
|
|
115
|
+
Message.from_hash(hash["message"] || hash[:message])
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
instance
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAcp
|
|
4
|
+
module Models
|
|
5
|
+
# Base class for all ACP models providing common serialization.
|
|
6
|
+
#
|
|
7
|
+
# Provides a simple DSL for declaring attributes with defaults,
|
|
8
|
+
# automatic JSON serialization/deserialization, and equality comparisons.
|
|
9
|
+
#
|
|
10
|
+
# @example Defining a model
|
|
11
|
+
# class MyModel < Base
|
|
12
|
+
# attribute :name, required: true
|
|
13
|
+
# attribute :count, default: 0
|
|
14
|
+
# attribute :tags, default: -> { [] }
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @abstract Subclass and use {.attribute} to define attributes
|
|
18
|
+
class Base
|
|
19
|
+
class << self
|
|
20
|
+
# Define an attribute on this model.
|
|
21
|
+
#
|
|
22
|
+
# @param name [Symbol] the attribute name
|
|
23
|
+
# @param type [Class, nil] optional type hint (for documentation)
|
|
24
|
+
# @param default [Object, Proc, nil] default value or proc
|
|
25
|
+
# @param required [Boolean] whether the attribute is required
|
|
26
|
+
# @return [void]
|
|
27
|
+
def attribute(name, type: nil, default: nil, required: false)
|
|
28
|
+
@attributes ||= {}
|
|
29
|
+
@attributes[name] = { type: type, default: default, required: required }
|
|
30
|
+
|
|
31
|
+
attr_reader name
|
|
32
|
+
|
|
33
|
+
define_method(:"#{name}=") do |value|
|
|
34
|
+
instance_variable_set(:"@#{name}", value)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get all attributes including inherited ones.
|
|
39
|
+
#
|
|
40
|
+
# @return [Hash<Symbol, Hash>] attribute definitions
|
|
41
|
+
def attributes
|
|
42
|
+
@attributes ||= {}
|
|
43
|
+
|
|
44
|
+
# Inherit attributes from parent class
|
|
45
|
+
if superclass.respond_to?(:attributes)
|
|
46
|
+
superclass.attributes.merge(@attributes)
|
|
47
|
+
else
|
|
48
|
+
@attributes
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get only attributes defined on this class (not inherited).
|
|
53
|
+
#
|
|
54
|
+
# @return [Hash<Symbol, Hash>] attribute definitions
|
|
55
|
+
def own_attributes
|
|
56
|
+
@attributes ||= {}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Create an instance from a hash.
|
|
60
|
+
#
|
|
61
|
+
# @param hash [Hash, nil] attribute values (string or symbol keys)
|
|
62
|
+
# @return [Base, nil] the new instance or nil if hash is nil
|
|
63
|
+
def from_hash(hash)
|
|
64
|
+
return nil if hash.nil?
|
|
65
|
+
|
|
66
|
+
instance = new
|
|
67
|
+
attributes.each do |name, _opts|
|
|
68
|
+
key = name.to_s
|
|
69
|
+
value = hash.key?(key) ? hash[key] : hash[name]
|
|
70
|
+
instance.send(:"#{name}=", value) unless value.nil?
|
|
71
|
+
end
|
|
72
|
+
instance
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
alias from_h from_hash
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Initialize with keyword arguments.
|
|
79
|
+
#
|
|
80
|
+
# @param kwargs [Hash] attribute values
|
|
81
|
+
def initialize(**kwargs)
|
|
82
|
+
self.class.attributes.each do |name, opts|
|
|
83
|
+
value = kwargs.fetch(name) { opts[:default].is_a?(Proc) ? opts[:default].call : opts[:default] }
|
|
84
|
+
instance_variable_set(:"@#{name}", value)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Convert to a hash for JSON serialization.
|
|
89
|
+
#
|
|
90
|
+
# @return [Hash] attribute values (nil values excluded)
|
|
91
|
+
def to_h
|
|
92
|
+
self.class.attributes.each_with_object({}) do |(name, _opts), hash|
|
|
93
|
+
value = send(name)
|
|
94
|
+
next if value.nil?
|
|
95
|
+
|
|
96
|
+
hash[name] = serialize_value(value)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Convert to JSON string.
|
|
101
|
+
#
|
|
102
|
+
# @param args [Array] arguments passed to Hash#to_json
|
|
103
|
+
# @return [String] JSON representation
|
|
104
|
+
def to_json(*args)
|
|
105
|
+
to_h.to_json(*args)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Check equality based on all attributes.
|
|
109
|
+
#
|
|
110
|
+
# @param other [Object] object to compare
|
|
111
|
+
# @return [Boolean] true if same class and all attributes equal
|
|
112
|
+
def ==(other)
|
|
113
|
+
return false unless other.is_a?(self.class)
|
|
114
|
+
|
|
115
|
+
self.class.attributes.keys.all? do |name|
|
|
116
|
+
send(name) == other.send(name)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
alias eql? ==
|
|
121
|
+
|
|
122
|
+
# Compute hash based on all attributes.
|
|
123
|
+
#
|
|
124
|
+
# @return [Integer] hash code
|
|
125
|
+
def hash
|
|
126
|
+
self.class.attributes.keys.map { |name| send(name) }.hash
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def serialize_value(value)
|
|
132
|
+
case value
|
|
133
|
+
when Base
|
|
134
|
+
value.to_h
|
|
135
|
+
when Array
|
|
136
|
+
value.map { |v| serialize_value(v) }
|
|
137
|
+
when Hash
|
|
138
|
+
value.transform_values { |v| serialize_value(v) }
|
|
139
|
+
when Time
|
|
140
|
+
value.utc.iso8601
|
|
141
|
+
else
|
|
142
|
+
value
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAcp
|
|
4
|
+
module Models
|
|
5
|
+
# Error codes as defined in the ACP specification.
|
|
6
|
+
module ErrorCode
|
|
7
|
+
# Internal server error
|
|
8
|
+
SERVER_ERROR = "server_error"
|
|
9
|
+
# Invalid input from client
|
|
10
|
+
INVALID_INPUT = "invalid_input"
|
|
11
|
+
# Requested resource not found
|
|
12
|
+
NOT_FOUND = "not_found"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Structured error response following the ACP specification.
|
|
16
|
+
#
|
|
17
|
+
# @example Creating an error
|
|
18
|
+
# error = Error.server_error("Something went wrong")
|
|
19
|
+
# error.code # => "server_error"
|
|
20
|
+
# error.message # => "Something went wrong"
|
|
21
|
+
class Error
|
|
22
|
+
# @return [String] error code (server_error, invalid_input, not_found)
|
|
23
|
+
attr_reader :code
|
|
24
|
+
|
|
25
|
+
# @return [String] human-readable error message
|
|
26
|
+
attr_reader :message
|
|
27
|
+
|
|
28
|
+
# @return [Object, nil] additional error data
|
|
29
|
+
attr_reader :data
|
|
30
|
+
|
|
31
|
+
# Create a new error.
|
|
32
|
+
#
|
|
33
|
+
# @param code [String] error code
|
|
34
|
+
# @param message [String] error message
|
|
35
|
+
# @param data [Object, nil] additional data
|
|
36
|
+
def initialize(code:, message:, data: nil)
|
|
37
|
+
@code = code
|
|
38
|
+
@message = message
|
|
39
|
+
@data = data
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Convert to hash for JSON serialization.
|
|
43
|
+
#
|
|
44
|
+
# @return [Hash] error as hash
|
|
45
|
+
def to_h
|
|
46
|
+
hash = {
|
|
47
|
+
code: @code,
|
|
48
|
+
message: @message
|
|
49
|
+
}
|
|
50
|
+
hash[:data] = @data if @data
|
|
51
|
+
hash
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Convert to JSON string.
|
|
55
|
+
#
|
|
56
|
+
# @param args [Array] arguments passed to Hash#to_json
|
|
57
|
+
# @return [String] JSON representation
|
|
58
|
+
def to_json(*args)
|
|
59
|
+
to_h.to_json(*args)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Create from a hash (JSON deserialization).
|
|
63
|
+
#
|
|
64
|
+
# @param hash [Hash] error data
|
|
65
|
+
# @return [Error] the error
|
|
66
|
+
def self.from_hash(hash)
|
|
67
|
+
new(
|
|
68
|
+
code: hash["code"] || hash[:code],
|
|
69
|
+
message: hash["message"] || hash[:message],
|
|
70
|
+
data: hash["data"] || hash[:data]
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Create a server error.
|
|
75
|
+
#
|
|
76
|
+
# @param message [String] error message
|
|
77
|
+
# @param data [Object, nil] additional data
|
|
78
|
+
# @return [Error] the error
|
|
79
|
+
def self.server_error(message, data: nil)
|
|
80
|
+
new(code: ErrorCode::SERVER_ERROR, message: message, data: data)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Create an invalid input error.
|
|
84
|
+
#
|
|
85
|
+
# @param message [String] error message
|
|
86
|
+
# @param data [Object, nil] additional data
|
|
87
|
+
# @return [Error] the error
|
|
88
|
+
def self.invalid_input(message, data: nil)
|
|
89
|
+
new(code: ErrorCode::INVALID_INPUT, message: message, data: data)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Create a not found error.
|
|
93
|
+
#
|
|
94
|
+
# @param message [String] error message
|
|
95
|
+
# @param data [Object, nil] additional data
|
|
96
|
+
# @return [Error] the error
|
|
97
|
+
def self.not_found(message, data: nil)
|
|
98
|
+
new(code: ErrorCode::NOT_FOUND, message: message, data: data)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAcp
|
|
4
|
+
module Models
|
|
5
|
+
# Base class for all Server-Sent Events (SSE).
|
|
6
|
+
#
|
|
7
|
+
# Events are streamed during run execution to provide
|
|
8
|
+
# real-time updates on progress and output.
|
|
9
|
+
#
|
|
10
|
+
# @abstract Subclass and set type in initializer
|
|
11
|
+
class Event < Base
|
|
12
|
+
# @!attribute [r] type
|
|
13
|
+
# @return [String] event type (see Types::EventType)
|
|
14
|
+
attribute :type, required: true
|
|
15
|
+
|
|
16
|
+
# Format for Server-Sent Events protocol.
|
|
17
|
+
#
|
|
18
|
+
# @return [String] SSE-formatted event
|
|
19
|
+
def sse_format
|
|
20
|
+
"event: #{@type}\ndata: #{to_json}\n\n"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Message created event
|
|
25
|
+
class MessageCreatedEvent < Event
|
|
26
|
+
attribute :message
|
|
27
|
+
|
|
28
|
+
def initialize(**kwargs)
|
|
29
|
+
kwargs[:type] = Types::EventType::MESSAGE_CREATED
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.from_hash(hash)
|
|
34
|
+
return nil if hash.nil?
|
|
35
|
+
|
|
36
|
+
instance = super
|
|
37
|
+
if hash["message"] || hash[:message]
|
|
38
|
+
instance.message = Message.from_hash(hash["message"] || hash[:message])
|
|
39
|
+
end
|
|
40
|
+
instance
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Message part event (for streaming individual parts)
|
|
45
|
+
class MessagePartEvent < Event
|
|
46
|
+
attribute :part
|
|
47
|
+
|
|
48
|
+
def initialize(**kwargs)
|
|
49
|
+
kwargs[:type] = Types::EventType::MESSAGE_PART
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.from_hash(hash)
|
|
54
|
+
return nil if hash.nil?
|
|
55
|
+
|
|
56
|
+
instance = super
|
|
57
|
+
if hash["part"] || hash[:part]
|
|
58
|
+
instance.part = MessagePart.from_hash(hash["part"] || hash[:part])
|
|
59
|
+
end
|
|
60
|
+
instance
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Message completed event
|
|
65
|
+
class MessageCompletedEvent < Event
|
|
66
|
+
attribute :message
|
|
67
|
+
|
|
68
|
+
def initialize(**kwargs)
|
|
69
|
+
kwargs[:type] = Types::EventType::MESSAGE_COMPLETED
|
|
70
|
+
super
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.from_hash(hash)
|
|
74
|
+
return nil if hash.nil?
|
|
75
|
+
|
|
76
|
+
instance = super
|
|
77
|
+
if hash["message"] || hash[:message]
|
|
78
|
+
instance.message = Message.from_hash(hash["message"] || hash[:message])
|
|
79
|
+
end
|
|
80
|
+
instance
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Run created event
|
|
85
|
+
class RunCreatedEvent < Event
|
|
86
|
+
attribute :run
|
|
87
|
+
|
|
88
|
+
def initialize(**kwargs)
|
|
89
|
+
kwargs[:type] = Types::EventType::RUN_CREATED
|
|
90
|
+
super
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.from_hash(hash)
|
|
94
|
+
return nil if hash.nil?
|
|
95
|
+
|
|
96
|
+
instance = super
|
|
97
|
+
if hash["run"] || hash[:run]
|
|
98
|
+
instance.run = Run.from_hash(hash["run"] || hash[:run])
|
|
99
|
+
end
|
|
100
|
+
instance
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Run in-progress event
|
|
105
|
+
class RunInProgressEvent < Event
|
|
106
|
+
attribute :run_id
|
|
107
|
+
|
|
108
|
+
def initialize(**kwargs)
|
|
109
|
+
kwargs[:type] = Types::EventType::RUN_IN_PROGRESS
|
|
110
|
+
super
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Run awaiting event
|
|
115
|
+
class RunAwaitingEvent < Event
|
|
116
|
+
attribute :run_id
|
|
117
|
+
attribute :await_request
|
|
118
|
+
|
|
119
|
+
def initialize(**kwargs)
|
|
120
|
+
kwargs[:type] = Types::EventType::RUN_AWAITING
|
|
121
|
+
super
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.from_hash(hash)
|
|
125
|
+
return nil if hash.nil?
|
|
126
|
+
|
|
127
|
+
instance = super
|
|
128
|
+
if hash["await_request"] || hash[:await_request]
|
|
129
|
+
instance.await_request = AwaitRequest.from_hash(hash["await_request"] || hash[:await_request])
|
|
130
|
+
end
|
|
131
|
+
instance
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Run completed event
|
|
136
|
+
class RunCompletedEvent < Event
|
|
137
|
+
attribute :run
|
|
138
|
+
|
|
139
|
+
def initialize(**kwargs)
|
|
140
|
+
kwargs[:type] = Types::EventType::RUN_COMPLETED
|
|
141
|
+
super
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.from_hash(hash)
|
|
145
|
+
return nil if hash.nil?
|
|
146
|
+
|
|
147
|
+
instance = super
|
|
148
|
+
if hash["run"] || hash[:run]
|
|
149
|
+
instance.run = Run.from_hash(hash["run"] || hash[:run])
|
|
150
|
+
end
|
|
151
|
+
instance
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Run cancelled event
|
|
156
|
+
class RunCancelledEvent < Event
|
|
157
|
+
attribute :run_id
|
|
158
|
+
|
|
159
|
+
def initialize(**kwargs)
|
|
160
|
+
kwargs[:type] = Types::EventType::RUN_CANCELLED
|
|
161
|
+
super
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Run failed event
|
|
166
|
+
class RunFailedEvent < Event
|
|
167
|
+
attribute :run_id
|
|
168
|
+
attribute :error
|
|
169
|
+
|
|
170
|
+
def initialize(**kwargs)
|
|
171
|
+
kwargs[:type] = Types::EventType::RUN_FAILED
|
|
172
|
+
super
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.from_hash(hash)
|
|
176
|
+
return nil if hash.nil?
|
|
177
|
+
|
|
178
|
+
instance = super
|
|
179
|
+
if hash["error"] || hash[:error]
|
|
180
|
+
instance.error = Error.from_hash(hash["error"] || hash[:error])
|
|
181
|
+
end
|
|
182
|
+
instance
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Generic event
|
|
187
|
+
class GenericEvent < Event
|
|
188
|
+
attribute :data
|
|
189
|
+
|
|
190
|
+
def initialize(**kwargs)
|
|
191
|
+
kwargs[:type] = Types::EventType::GENERIC
|
|
192
|
+
super
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Error event
|
|
197
|
+
class ErrorEvent < Event
|
|
198
|
+
attribute :error
|
|
199
|
+
|
|
200
|
+
def initialize(**kwargs)
|
|
201
|
+
kwargs[:type] = Types::EventType::ERROR
|
|
202
|
+
super
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def self.from_hash(hash)
|
|
206
|
+
return nil if hash.nil?
|
|
207
|
+
|
|
208
|
+
instance = super
|
|
209
|
+
if hash["error"] || hash[:error]
|
|
210
|
+
instance.error = Error.from_hash(hash["error"] || hash[:error])
|
|
211
|
+
end
|
|
212
|
+
instance
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Factory module for parsing events from hashes.
|
|
217
|
+
module Events
|
|
218
|
+
# Mapping of event types to classes
|
|
219
|
+
EVENT_CLASSES = {
|
|
220
|
+
Types::EventType::MESSAGE_CREATED => MessageCreatedEvent,
|
|
221
|
+
Types::EventType::MESSAGE_PART => MessagePartEvent,
|
|
222
|
+
Types::EventType::MESSAGE_COMPLETED => MessageCompletedEvent,
|
|
223
|
+
Types::EventType::RUN_CREATED => RunCreatedEvent,
|
|
224
|
+
Types::EventType::RUN_IN_PROGRESS => RunInProgressEvent,
|
|
225
|
+
Types::EventType::RUN_AWAITING => RunAwaitingEvent,
|
|
226
|
+
Types::EventType::RUN_COMPLETED => RunCompletedEvent,
|
|
227
|
+
Types::EventType::RUN_CANCELLED => RunCancelledEvent,
|
|
228
|
+
Types::EventType::RUN_FAILED => RunFailedEvent,
|
|
229
|
+
Types::EventType::GENERIC => GenericEvent,
|
|
230
|
+
Types::EventType::ERROR => ErrorEvent
|
|
231
|
+
}.freeze
|
|
232
|
+
|
|
233
|
+
# Parse an event from a hash.
|
|
234
|
+
#
|
|
235
|
+
# @param hash [Hash, nil] event data with "type" key
|
|
236
|
+
# @return [Event, nil] the appropriate event subclass or nil
|
|
237
|
+
def self.from_hash(hash)
|
|
238
|
+
return nil if hash.nil?
|
|
239
|
+
|
|
240
|
+
type = hash["type"] || hash[:type]
|
|
241
|
+
klass = EVENT_CLASSES[type] || GenericEvent
|
|
242
|
+
klass.from_hash(hash)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Parse JSON SSE data.
|
|
246
|
+
#
|
|
247
|
+
# @param data [String] JSON string
|
|
248
|
+
# @return [Hash, nil] parsed hash or nil if invalid
|
|
249
|
+
def self.parse_sse(data)
|
|
250
|
+
JSON.parse(data)
|
|
251
|
+
rescue JSON::ParserError
|
|
252
|
+
nil
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|