language-operator 0.0.1 → 0.1.30
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 +4 -4
- data/.rubocop.yml +125 -0
- data/CHANGELOG.md +53 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +284 -0
- data/LICENSE +229 -21
- data/Makefile +77 -0
- data/README.md +3 -11
- data/Rakefile +34 -0
- data/bin/aictl +7 -0
- data/completions/_aictl +232 -0
- data/completions/aictl.bash +121 -0
- data/completions/aictl.fish +114 -0
- data/docs/architecture/agent-runtime.md +585 -0
- data/docs/dsl/agent-reference.md +591 -0
- data/docs/dsl/best-practices.md +1078 -0
- data/docs/dsl/chat-endpoints.md +895 -0
- data/docs/dsl/constraints.md +671 -0
- data/docs/dsl/mcp-integration.md +1177 -0
- data/docs/dsl/webhooks.md +932 -0
- data/docs/dsl/workflows.md +744 -0
- data/examples/README.md +569 -0
- data/examples/agent_example.rb +86 -0
- data/examples/chat_endpoint_agent.rb +118 -0
- data/examples/github_webhook_agent.rb +171 -0
- data/examples/mcp_agent.rb +158 -0
- data/examples/oauth_callback_agent.rb +296 -0
- data/examples/stripe_webhook_agent.rb +219 -0
- data/examples/webhook_agent.rb +80 -0
- data/lib/language_operator/agent/base.rb +110 -0
- data/lib/language_operator/agent/executor.rb +440 -0
- data/lib/language_operator/agent/instrumentation.rb +54 -0
- data/lib/language_operator/agent/metrics_tracker.rb +183 -0
- data/lib/language_operator/agent/safety/ast_validator.rb +272 -0
- data/lib/language_operator/agent/safety/audit_logger.rb +104 -0
- data/lib/language_operator/agent/safety/budget_tracker.rb +175 -0
- data/lib/language_operator/agent/safety/content_filter.rb +93 -0
- data/lib/language_operator/agent/safety/manager.rb +207 -0
- data/lib/language_operator/agent/safety/rate_limiter.rb +150 -0
- data/lib/language_operator/agent/safety/safe_executor.rb +115 -0
- data/lib/language_operator/agent/scheduler.rb +183 -0
- data/lib/language_operator/agent/telemetry.rb +116 -0
- data/lib/language_operator/agent/web_server.rb +610 -0
- data/lib/language_operator/agent/webhook_authenticator.rb +226 -0
- data/lib/language_operator/agent.rb +149 -0
- data/lib/language_operator/cli/commands/agent.rb +1252 -0
- data/lib/language_operator/cli/commands/cluster.rb +335 -0
- data/lib/language_operator/cli/commands/install.rb +404 -0
- data/lib/language_operator/cli/commands/model.rb +266 -0
- data/lib/language_operator/cli/commands/persona.rb +396 -0
- data/lib/language_operator/cli/commands/quickstart.rb +22 -0
- data/lib/language_operator/cli/commands/status.rb +156 -0
- data/lib/language_operator/cli/commands/tool.rb +537 -0
- data/lib/language_operator/cli/commands/use.rb +47 -0
- data/lib/language_operator/cli/errors/handler.rb +180 -0
- data/lib/language_operator/cli/errors/suggestions.rb +176 -0
- data/lib/language_operator/cli/formatters/code_formatter.rb +81 -0
- data/lib/language_operator/cli/formatters/log_formatter.rb +290 -0
- data/lib/language_operator/cli/formatters/progress_formatter.rb +53 -0
- data/lib/language_operator/cli/formatters/table_formatter.rb +179 -0
- data/lib/language_operator/cli/formatters/value_formatter.rb +113 -0
- data/lib/language_operator/cli/helpers/cluster_context.rb +62 -0
- data/lib/language_operator/cli/helpers/cluster_validator.rb +101 -0
- data/lib/language_operator/cli/helpers/editor_helper.rb +58 -0
- data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +167 -0
- data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +74 -0
- data/lib/language_operator/cli/helpers/schedule_builder.rb +108 -0
- data/lib/language_operator/cli/helpers/user_prompts.rb +69 -0
- data/lib/language_operator/cli/main.rb +232 -0
- data/lib/language_operator/cli/templates/tools/generic.yaml +66 -0
- data/lib/language_operator/cli/wizards/agent_wizard.rb +246 -0
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +588 -0
- data/lib/language_operator/client/base.rb +214 -0
- data/lib/language_operator/client/config.rb +136 -0
- data/lib/language_operator/client/cost_calculator.rb +37 -0
- data/lib/language_operator/client/mcp_connector.rb +123 -0
- data/lib/language_operator/client.rb +19 -0
- data/lib/language_operator/config/cluster_config.rb +101 -0
- data/lib/language_operator/config/tool_patterns.yaml +57 -0
- data/lib/language_operator/config/tool_registry.rb +96 -0
- data/lib/language_operator/config.rb +138 -0
- data/lib/language_operator/dsl/adapter.rb +124 -0
- data/lib/language_operator/dsl/agent_context.rb +90 -0
- data/lib/language_operator/dsl/agent_definition.rb +427 -0
- data/lib/language_operator/dsl/chat_endpoint_definition.rb +115 -0
- data/lib/language_operator/dsl/config.rb +119 -0
- data/lib/language_operator/dsl/context.rb +50 -0
- data/lib/language_operator/dsl/execution_context.rb +47 -0
- data/lib/language_operator/dsl/helpers.rb +109 -0
- data/lib/language_operator/dsl/http.rb +184 -0
- data/lib/language_operator/dsl/mcp_server_definition.rb +73 -0
- data/lib/language_operator/dsl/parameter_definition.rb +124 -0
- data/lib/language_operator/dsl/registry.rb +36 -0
- data/lib/language_operator/dsl/shell.rb +125 -0
- data/lib/language_operator/dsl/tool_definition.rb +112 -0
- data/lib/language_operator/dsl/webhook_authentication.rb +114 -0
- data/lib/language_operator/dsl/webhook_definition.rb +106 -0
- data/lib/language_operator/dsl/workflow_definition.rb +259 -0
- data/lib/language_operator/dsl.rb +160 -0
- data/lib/language_operator/errors.rb +60 -0
- data/lib/language_operator/kubernetes/client.rb +279 -0
- data/lib/language_operator/kubernetes/resource_builder.rb +194 -0
- data/lib/language_operator/loggable.rb +47 -0
- data/lib/language_operator/logger.rb +141 -0
- data/lib/language_operator/retry.rb +123 -0
- data/lib/language_operator/retryable.rb +132 -0
- data/lib/language_operator/tool_loader.rb +242 -0
- data/lib/language_operator/validators.rb +170 -0
- data/lib/language_operator/version.rb +1 -1
- data/lib/language_operator.rb +65 -3
- data/requirements/tasks/challenge.md +9 -0
- data/requirements/tasks/iterate.md +36 -0
- data/requirements/tasks/optimize.md +21 -0
- data/requirements/tasks/tag.md +5 -0
- data/test_agent_dsl.rb +108 -0
- metadata +503 -20
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LanguageOperator
|
|
4
|
+
# Common parameter validation utilities
|
|
5
|
+
#
|
|
6
|
+
# Provides reusable validators for common parameter validation patterns
|
|
7
|
+
# across tools. All validators follow the same convention:
|
|
8
|
+
# - Return `nil` if the value is valid
|
|
9
|
+
# - Return an error string if the value is invalid
|
|
10
|
+
#
|
|
11
|
+
# This allows for consistent usage in tools:
|
|
12
|
+
# error = LanguageOperator::Validators.http_url(params['url'])
|
|
13
|
+
# next error if error
|
|
14
|
+
#
|
|
15
|
+
# @example URL validation
|
|
16
|
+
# error = Validators.http_url('https://example.com')
|
|
17
|
+
# # => nil (valid)
|
|
18
|
+
#
|
|
19
|
+
# error = Validators.http_url('ftp://example.com')
|
|
20
|
+
# # => "Error: Invalid URL. Must start with http:// or https://"
|
|
21
|
+
#
|
|
22
|
+
# @example Enum validation
|
|
23
|
+
# error = Validators.one_of('GET', %w[GET POST PUT DELETE], 'HTTP method')
|
|
24
|
+
# # => nil (valid)
|
|
25
|
+
#
|
|
26
|
+
# error = Validators.one_of('INVALID', %w[GET POST PUT DELETE], 'HTTP method')
|
|
27
|
+
# # => "Error: Invalid HTTP method 'INVALID'. Expected: one of: GET, POST, PUT, DELETE"
|
|
28
|
+
module Validators
|
|
29
|
+
# Validate HTTP/HTTPS URL format
|
|
30
|
+
#
|
|
31
|
+
# @param url [String] URL to validate
|
|
32
|
+
# @return [String, nil] Error message if invalid, nil if valid
|
|
33
|
+
#
|
|
34
|
+
# @example Valid URLs
|
|
35
|
+
# Validators.http_url('http://example.com') # => nil
|
|
36
|
+
# Validators.http_url('https://api.example.com/v1') # => nil
|
|
37
|
+
#
|
|
38
|
+
# @example Invalid URLs
|
|
39
|
+
# Validators.http_url('') # => "Error: URL cannot be empty"
|
|
40
|
+
# Validators.http_url('ftp://example.com') # => "Error: Invalid URL..."
|
|
41
|
+
def self.http_url(url)
|
|
42
|
+
return 'Error: URL cannot be empty' if url.nil? || url.strip.empty?
|
|
43
|
+
return nil if url =~ %r{^https?://.+}
|
|
44
|
+
|
|
45
|
+
'Error: Invalid URL. Must start with http:// or https://'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Validate that a value is not nil or empty
|
|
49
|
+
#
|
|
50
|
+
# @param value [Object] Value to check
|
|
51
|
+
# @param field_name [String] Name of the field for error message
|
|
52
|
+
# @return [String, nil] Error message if invalid, nil if valid
|
|
53
|
+
#
|
|
54
|
+
# @example Valid values
|
|
55
|
+
# Validators.not_empty('value', 'Name') # => nil
|
|
56
|
+
# Validators.not_empty(' text ', 'Name') # => nil (has content after strip)
|
|
57
|
+
#
|
|
58
|
+
# @example Invalid values
|
|
59
|
+
# Validators.not_empty(nil, 'Name') # => "Error: Name cannot be empty"
|
|
60
|
+
# Validators.not_empty('', 'Name') # => "Error: Name cannot be empty"
|
|
61
|
+
# Validators.not_empty(' ', 'Name') # => "Error: Name cannot be empty"
|
|
62
|
+
def self.not_empty(value, field_name)
|
|
63
|
+
return nil if value && !value.to_s.strip.empty?
|
|
64
|
+
|
|
65
|
+
LanguageOperator::Errors.empty_field(field_name)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Validate that a value is one of the allowed options
|
|
69
|
+
#
|
|
70
|
+
# @param value [String] Value to validate
|
|
71
|
+
# @param allowed [Array<String>] Allowed values
|
|
72
|
+
# @param field_name [String] Field name for error message
|
|
73
|
+
# @return [String, nil] Error message if invalid, nil if valid
|
|
74
|
+
#
|
|
75
|
+
# @example Valid choice
|
|
76
|
+
# Validators.one_of('GET', %w[GET POST PUT], 'method') # => nil
|
|
77
|
+
#
|
|
78
|
+
# @example Invalid choice
|
|
79
|
+
# Validators.one_of('DELETE', %w[GET POST PUT], 'method')
|
|
80
|
+
# # => "Error: Invalid method 'DELETE'. Expected: one of: GET, POST, PUT"
|
|
81
|
+
def self.one_of(value, allowed, field_name)
|
|
82
|
+
return nil if allowed.include?(value)
|
|
83
|
+
|
|
84
|
+
LanguageOperator::Errors.invalid_parameter(
|
|
85
|
+
field_name,
|
|
86
|
+
value,
|
|
87
|
+
"one of: #{allowed.join(', ')}"
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Validate numeric range
|
|
92
|
+
#
|
|
93
|
+
# @param value [Numeric] Value to validate
|
|
94
|
+
# @param min [Numeric, nil] Minimum value (inclusive)
|
|
95
|
+
# @param max [Numeric, nil] Maximum value (inclusive)
|
|
96
|
+
# @param field_name [String] Field name for error message
|
|
97
|
+
# @return [String, nil] Error message if invalid, nil if valid
|
|
98
|
+
#
|
|
99
|
+
# @example Valid ranges
|
|
100
|
+
# Validators.numeric_range(5, min: 1, max: 10) # => nil
|
|
101
|
+
# Validators.numeric_range(100, min: 50) # => nil
|
|
102
|
+
# Validators.numeric_range(5, max: 10) # => nil
|
|
103
|
+
#
|
|
104
|
+
# @example Invalid ranges
|
|
105
|
+
# Validators.numeric_range('foo', min: 1) # => "Error: value must be a number"
|
|
106
|
+
# Validators.numeric_range(0, min: 1) # => "Error: value must be at least 1"
|
|
107
|
+
# Validators.numeric_range(100, max: 50) # => "Error: value must be at most 50"
|
|
108
|
+
def self.numeric_range(value, min: nil, max: nil, field_name: 'value')
|
|
109
|
+
return "Error: #{field_name} must be a number" unless value.is_a?(Numeric)
|
|
110
|
+
|
|
111
|
+
return "Error: #{field_name} must be at least #{min}" if min && value < min
|
|
112
|
+
|
|
113
|
+
return "Error: #{field_name} must be at most #{max}" if max && value > max
|
|
114
|
+
|
|
115
|
+
nil
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Validate email address format (basic)
|
|
119
|
+
#
|
|
120
|
+
# Note: This is a basic format check, not full RFC 5322 validation.
|
|
121
|
+
# It checks for: something@domain.tld
|
|
122
|
+
#
|
|
123
|
+
# @param email [String] Email address to validate
|
|
124
|
+
# @return [String, nil] Error message if invalid, nil if valid
|
|
125
|
+
#
|
|
126
|
+
# @example Valid emails
|
|
127
|
+
# Validators.email('user@example.com') # => nil
|
|
128
|
+
# Validators.email('test.user+tag@domain.co.uk') # => nil
|
|
129
|
+
#
|
|
130
|
+
# @example Invalid emails
|
|
131
|
+
# Validators.email('') # => "Error: Email address cannot be empty"
|
|
132
|
+
# Validators.email('invalid') # => "Error: Invalid email address format"
|
|
133
|
+
# Validators.email('@example.com') # => "Error: Invalid email address format"
|
|
134
|
+
def self.email(email)
|
|
135
|
+
return 'Error: Email address cannot be empty' if email.nil? || email.strip.empty?
|
|
136
|
+
return nil if email =~ /\A[^@\s]+@[^@\s]+\.[^@\s]+\z/
|
|
137
|
+
|
|
138
|
+
'Error: Invalid email address format'
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Validate that a path doesn't contain directory traversal attempts
|
|
142
|
+
#
|
|
143
|
+
# Checks for:
|
|
144
|
+
# - Parent directory references (..)
|
|
145
|
+
# - Null bytes (\0)
|
|
146
|
+
#
|
|
147
|
+
# Note: This is a basic safety check. Tool-specific path validation
|
|
148
|
+
# (like workspace sandboxing) should still be implemented in the tools.
|
|
149
|
+
#
|
|
150
|
+
# @param path [String] Path to validate
|
|
151
|
+
# @return [String, nil] Error message if invalid, nil if valid
|
|
152
|
+
#
|
|
153
|
+
# @example Valid paths
|
|
154
|
+
# Validators.safe_path('/absolute/path') # => nil
|
|
155
|
+
# Validators.safe_path('relative/path.txt') # => nil
|
|
156
|
+
#
|
|
157
|
+
# @example Invalid paths
|
|
158
|
+
# Validators.safe_path('') # => "Error: Path cannot be empty"
|
|
159
|
+
# Validators.safe_path('../../../etc/passwd') # => "Error: Path contains..."
|
|
160
|
+
# Validators.safe_path("file\0name") # => "Error: Path contains..."
|
|
161
|
+
def self.safe_path(path)
|
|
162
|
+
return 'Error: Path cannot be empty' if path.nil? || path.strip.empty?
|
|
163
|
+
|
|
164
|
+
# Check for obvious traversal attempts
|
|
165
|
+
return 'Error: Path contains invalid characters or directory traversal' if path.include?('..') || path.include?("\0")
|
|
166
|
+
|
|
167
|
+
nil
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
data/lib/language_operator.rb
CHANGED
|
@@ -1,8 +1,70 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require 'language_operator/version'
|
|
4
|
+
require 'language_operator/errors'
|
|
5
|
+
require 'language_operator/retry'
|
|
6
|
+
require 'language_operator/config'
|
|
7
|
+
require 'language_operator/validators'
|
|
8
|
+
require 'language_operator/dsl'
|
|
9
|
+
require 'language_operator/client'
|
|
10
|
+
require 'language_operator/tool_loader'
|
|
4
11
|
|
|
5
|
-
#
|
|
6
|
-
#
|
|
12
|
+
# Agent module is optional - only load if dependencies are available
|
|
13
|
+
# This allows the SDK to be used in environments without agent dependencies
|
|
14
|
+
begin
|
|
15
|
+
require 'language_operator/agent'
|
|
16
|
+
rescue LoadError => e
|
|
17
|
+
# Agent dependencies not available, skip loading
|
|
18
|
+
warn "LanguageOperator: Agent module not loaded (missing dependency: #{e.message})" if ENV['DEBUG']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Langop - Ruby SDK for building MCP tools and language agents
|
|
22
|
+
#
|
|
23
|
+
# This gem provides:
|
|
24
|
+
# - DSL for defining MCP tools with a clean, Ruby-like syntax
|
|
25
|
+
# - Client library for connecting to MCP servers
|
|
26
|
+
# - Agent framework for autonomous task execution
|
|
27
|
+
# - CLI for generating and running tools and agents
|
|
28
|
+
#
|
|
29
|
+
# @example Define a tool
|
|
30
|
+
# LanguageOperator::Dsl.define do
|
|
31
|
+
# tool "greet" do
|
|
32
|
+
# description "Greet a user"
|
|
33
|
+
# parameter :name do
|
|
34
|
+
# type :string
|
|
35
|
+
# required true
|
|
36
|
+
# end
|
|
37
|
+
# execute do |params|
|
|
38
|
+
# "Hello, #{params['name']}!"
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# @example Use the client
|
|
44
|
+
# config = LanguageOperator::Client::Config.from_env
|
|
45
|
+
# client = LanguageOperator::Client::Base.new(config)
|
|
46
|
+
# client.connect!
|
|
47
|
+
# response = client.send_message("What can you do?")
|
|
48
|
+
#
|
|
49
|
+
# @example Run an agent
|
|
50
|
+
# agent = LanguageOperator::Agent::Base.new(config)
|
|
51
|
+
# agent.run
|
|
7
52
|
module LanguageOperator
|
|
53
|
+
class Error < StandardError; end
|
|
54
|
+
|
|
55
|
+
# Convenience method to define tools
|
|
56
|
+
#
|
|
57
|
+
# @yield Block containing tool definitions
|
|
58
|
+
# @return [LanguageOperator::Dsl::Registry] The global tool registry
|
|
59
|
+
def self.define(&)
|
|
60
|
+
Dsl.define(&)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Convenience method to load tools from a file
|
|
64
|
+
#
|
|
65
|
+
# @param file_path [String] Path to tool definition file
|
|
66
|
+
# @return [LanguageOperator::Dsl::Registry] The global tool registry
|
|
67
|
+
def self.load_file(file_path)
|
|
68
|
+
Dsl.load_file(file_path)
|
|
69
|
+
end
|
|
8
70
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
You are the public face, the user interface, of an extremely ambitious project called the Language Operator.
|
|
2
|
+
|
|
3
|
+
It synthesizes code *in production* securely from any arbitrary instructions written in plain text.
|
|
4
|
+
|
|
5
|
+
Read ../language-operator/README.md to understand your significance.
|
|
6
|
+
|
|
7
|
+
In that light, I challenge you to ensure that we're matching that vision in quality and supporting the full power of its CRDs.
|
|
8
|
+
|
|
9
|
+
Dive into this code, maybe into the language-operator itself. What can **this gem** do better at?
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Task
|
|
2
|
+
|
|
3
|
+
## Persona
|
|
4
|
+
|
|
5
|
+
Adopt the [ruby-engineer](../../../requirements/personas/ruby-engineer.md) persona while executing these instructions, please.
|
|
6
|
+
|
|
7
|
+
## Inputs
|
|
8
|
+
|
|
9
|
+
- id int -- A forgejo issue index ID.
|
|
10
|
+
|
|
11
|
+
## Background
|
|
12
|
+
|
|
13
|
+
This is a early-phase project that works exclusively in main.
|
|
14
|
+
Issues are found using the Forgejo MCP tools for this project:
|
|
15
|
+
- Owner: language-operator
|
|
16
|
+
- Repository: language-operator-gem
|
|
17
|
+
|
|
18
|
+
## Instructions
|
|
19
|
+
|
|
20
|
+
Follow these directions closely:
|
|
21
|
+
|
|
22
|
+
1. Use the ForgeJo tool to find the top issue for this repository.
|
|
23
|
+
2. Investigate if it's valid, or a mis-use of the intended feature.
|
|
24
|
+
3. **CRITICAL:** Switch to plan mode, and propose an implementation plan. Await my feedback.
|
|
25
|
+
4. Add your implementation plan as a comment on the issue.
|
|
26
|
+
5. Implement the changes.
|
|
27
|
+
6. Run existing tests, and add new ones if necessary. Remember to include CI. Remember the linter and that bundler will fail if it's out of sync with its lockfile.
|
|
28
|
+
7. **CRITICAL:** Test the actual functionality manually before committing. If it's a CLI command, run it. If it's library code, test it in the appropriate context. Never commit untested code.
|
|
29
|
+
8. Commit the change and push to origin.
|
|
30
|
+
9. **CRITICAL:** Halt while CI runs and await my feedback.
|
|
31
|
+
10. Comment on your solution in the ForgeJo issue.
|
|
32
|
+
11. Resolve the issue.
|
|
33
|
+
|
|
34
|
+
## Output
|
|
35
|
+
|
|
36
|
+
An implementation, test coverage, updated CI, and a closed ticket.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# Task
|
|
3
|
+
|
|
4
|
+
## Name
|
|
5
|
+
|
|
6
|
+
Optimize
|
|
7
|
+
|
|
8
|
+
## Persona
|
|
9
|
+
|
|
10
|
+
Adopt the [ruby-engineer](../requirements/personas/ruby-engineer.md) persona while executing these instructions, please.
|
|
11
|
+
|
|
12
|
+
## Instructions
|
|
13
|
+
|
|
14
|
+
Suggest an improvement that could improve the quality of the codebase or developer experience. Things like opportunities to reduce lines of code, DRYing up code, or eliminating dead code paths.
|
|
15
|
+
|
|
16
|
+
An important thing to consider is that this code has been written by different agents with different contexts, who may not have been aware of overall patterns. These kinds of optimizations are high priority.
|
|
17
|
+
|
|
18
|
+
## Output
|
|
19
|
+
|
|
20
|
+
Switch to Plan mode.
|
|
21
|
+
A proposed list of three concrete optimizations.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Time to release a new version!
|
|
2
|
+
|
|
3
|
+
1. Update the patch version in lib/language_operator/version.rb and re-run bundler to update the lockfile.
|
|
4
|
+
2. Commit the version with the message "vX.Y.Z" and push to origin.
|
|
5
|
+
3. Tag the new version and push to origin with the --tags argument.
|
data/test_agent_dsl.rb
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Quick test of the agent DSL without requiring full langop gem dependencies
|
|
5
|
+
|
|
6
|
+
require_relative 'lib/langop/dsl/agent_definition'
|
|
7
|
+
require_relative 'lib/langop/dsl/agent_context'
|
|
8
|
+
require_relative 'lib/langop/dsl/workflow_definition'
|
|
9
|
+
|
|
10
|
+
puts '=' * 70
|
|
11
|
+
puts 'Agent DSL Test'
|
|
12
|
+
puts '=' * 70
|
|
13
|
+
puts
|
|
14
|
+
|
|
15
|
+
# Create registry and context
|
|
16
|
+
registry = Langop::Dsl::AgentRegistry.new
|
|
17
|
+
context = Langop::Dsl::AgentContext.new(registry)
|
|
18
|
+
|
|
19
|
+
# Define an agent using the DSL (this is what the operator will synthesize)
|
|
20
|
+
context.agent 'kubernetes-news' do
|
|
21
|
+
description 'Daily Kubernetes news summarization agent'
|
|
22
|
+
|
|
23
|
+
# Persona (distilled by operator from LanguagePersona)
|
|
24
|
+
persona <<~PERSONA
|
|
25
|
+
You are a technical writer specializing in Kubernetes. When researching and
|
|
26
|
+
summarizing news, maintain a clear and precise tone, always cite your sources,
|
|
27
|
+
use proper technical terminology, and make complex topics accessible without
|
|
28
|
+
unnecessary jargon. Your summaries should be educational and well-structured.
|
|
29
|
+
PERSONA
|
|
30
|
+
|
|
31
|
+
# Schedule (extracted from: "once a day, preferably around lunchtime")
|
|
32
|
+
schedule '0 12 * * *'
|
|
33
|
+
|
|
34
|
+
# Objectives (extracted from instructions)
|
|
35
|
+
objectives [
|
|
36
|
+
'Search for recent Kubernetes news using web_search tool',
|
|
37
|
+
'Provide a concise summary of findings'
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# Workflow (synthesized by operator)
|
|
41
|
+
workflow do
|
|
42
|
+
step :search do
|
|
43
|
+
tool 'web_search'
|
|
44
|
+
params query: 'Kubernetes news latest'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
step :summarize do
|
|
48
|
+
depends_on :search
|
|
49
|
+
prompt 'Provide a concise summary of these Kubernetes news items: {search.output}'
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Constraints (inferred by operator)
|
|
54
|
+
constraints do
|
|
55
|
+
max_iterations 20
|
|
56
|
+
timeout '5m'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Output (inferred from workspace settings)
|
|
60
|
+
output do
|
|
61
|
+
workspace 'summaries/kubernetes-{date}.md'
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Retrieve and display the agent
|
|
66
|
+
agent = registry.get('kubernetes-news')
|
|
67
|
+
|
|
68
|
+
if agent
|
|
69
|
+
puts '✅ Agent Definition Loaded'
|
|
70
|
+
puts
|
|
71
|
+
puts "Name: #{agent.name}"
|
|
72
|
+
puts "Description: #{agent.description}"
|
|
73
|
+
puts "Mode: #{agent.execution_mode}"
|
|
74
|
+
puts "Schedule: #{agent.schedule}"
|
|
75
|
+
puts
|
|
76
|
+
puts '📋 Persona:'
|
|
77
|
+
puts agent.persona.lines.map { |l| " #{l}" }.join
|
|
78
|
+
puts
|
|
79
|
+
puts "🎯 Objectives (#{agent.objectives.size}):"
|
|
80
|
+
agent.objectives.each_with_index do |obj, i|
|
|
81
|
+
puts " #{i + 1}. #{obj}"
|
|
82
|
+
end
|
|
83
|
+
puts
|
|
84
|
+
puts "⚙️ Workflow Steps (#{agent.workflow.steps.size}):"
|
|
85
|
+
agent.workflow.steps.each do |name, step|
|
|
86
|
+
deps = step.dependencies.empty? ? '' : " [depends on: #{step.dependencies.join(', ')}]"
|
|
87
|
+
tool_info = step.tool ? " (tool: #{step.tool})" : ''
|
|
88
|
+
prompt_info = step.prompt ? " (prompt: #{step.prompt[0..50]}...)" : ''
|
|
89
|
+
puts " - #{name}#{deps}#{tool_info}#{prompt_info}"
|
|
90
|
+
end
|
|
91
|
+
puts
|
|
92
|
+
puts '📊 Constraints:'
|
|
93
|
+
agent.constraints.each do |key, value|
|
|
94
|
+
puts " #{key}: #{value}"
|
|
95
|
+
end
|
|
96
|
+
puts
|
|
97
|
+
puts '📤 Output:'
|
|
98
|
+
agent.output_config.each do |key, value|
|
|
99
|
+
puts " #{key}: #{value}"
|
|
100
|
+
end
|
|
101
|
+
puts
|
|
102
|
+
puts '=' * 70
|
|
103
|
+
puts '✅ DSL Test Complete - Agent ready for execution!'
|
|
104
|
+
puts '=' * 70
|
|
105
|
+
else
|
|
106
|
+
puts '❌ Failed to load agent'
|
|
107
|
+
exit 1
|
|
108
|
+
end
|