agentic 0.1.0 → 0.2.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.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.agentic.yml +2 -0
  3. data/.architecture/decisions/ArchitecturalFeatureBuilder.md +136 -0
  4. data/.architecture/decisions/ArchitectureConsiderations.md +200 -0
  5. data/.architecture/decisions/adr_001_observer_pattern_implementation.md +196 -0
  6. data/.architecture/decisions/adr_002_plan_orchestrator.md +320 -0
  7. data/.architecture/decisions/adr_003_plan_orchestrator_interface.md +179 -0
  8. data/.architecture/decisions/adrs/ADR-001-dependency-management.md +147 -0
  9. data/.architecture/decisions/adrs/ADR-002-system-boundaries.md +162 -0
  10. data/.architecture/decisions/adrs/ADR-003-content-safety.md +158 -0
  11. data/.architecture/decisions/adrs/ADR-004-agent-permissions.md +161 -0
  12. data/.architecture/decisions/adrs/ADR-005-adaptation-engine.md +127 -0
  13. data/.architecture/decisions/adrs/ADR-006-extension-system.md +273 -0
  14. data/.architecture/decisions/adrs/ADR-007-learning-system.md +156 -0
  15. data/.architecture/decisions/adrs/ADR-008-prompt-generation.md +325 -0
  16. data/.architecture/decisions/adrs/ADR-009-task-failure-handling.md +353 -0
  17. data/.architecture/decisions/adrs/ADR-010-task-input-handling.md +251 -0
  18. data/.architecture/decisions/adrs/ADR-011-task-observable-pattern.md +391 -0
  19. data/.architecture/decisions/adrs/ADR-012-task-output-handling.md +205 -0
  20. data/.architecture/decisions/adrs/ADR-013-architecture-alignment.md +211 -0
  21. data/.architecture/decisions/adrs/ADR-014-agent-capability-registry.md +80 -0
  22. data/.architecture/decisions/adrs/ADR-015-persistent-agent-store.md +100 -0
  23. data/.architecture/decisions/adrs/ADR-016-agent-assembly-engine.md +117 -0
  24. data/.architecture/decisions/adrs/ADR-017-streaming-observability.md +171 -0
  25. data/.architecture/decisions/capability_tools_distinction.md +150 -0
  26. data/.architecture/decisions/cli_command_structure.md +61 -0
  27. data/.architecture/implementation/agent_self_assembly_implementation.md +267 -0
  28. data/.architecture/implementation/agent_self_assembly_summary.md +138 -0
  29. data/.architecture/members.yml +187 -0
  30. data/.architecture/planning/self_implementation_exercise.md +295 -0
  31. data/.architecture/planning/session_compaction_rule.md +43 -0
  32. data/.architecture/planning/streaming_observability_feature.md +223 -0
  33. data/.architecture/principles.md +151 -0
  34. data/.architecture/recalibration/0-2-0.md +92 -0
  35. data/.architecture/recalibration/agent_self_assembly.md +238 -0
  36. data/.architecture/recalibration/cli_command_structure.md +91 -0
  37. data/.architecture/recalibration/implementation_roadmap_0-2-0.md +301 -0
  38. data/.architecture/recalibration/progress_tracking_0-2-0.md +114 -0
  39. data/.architecture/recalibration_process.md +127 -0
  40. data/.architecture/reviews/0-2-0.md +181 -0
  41. data/.architecture/reviews/cli_command_duplication.md +98 -0
  42. data/.architecture/templates/adr.md +105 -0
  43. data/.architecture/templates/implementation_roadmap.md +125 -0
  44. data/.architecture/templates/progress_tracking.md +89 -0
  45. data/.architecture/templates/recalibration_plan.md +70 -0
  46. data/.architecture/templates/version_comparison.md +124 -0
  47. data/.claude/settings.local.json +13 -0
  48. data/.claude-sessions/001-task-class-architecture-implementation.md +129 -0
  49. data/.claude-sessions/002-plan-orchestrator-interface-review.md +105 -0
  50. data/.claude-sessions/architecture-governance-implementation.md +37 -0
  51. data/.claude-sessions/architecture-review-session.md +27 -0
  52. data/ArchitecturalFeatureBuilder.md +136 -0
  53. data/ArchitectureConsiderations.md +229 -0
  54. data/CHANGELOG.md +57 -2
  55. data/CLAUDE.md +111 -0
  56. data/CONTRIBUTING.md +286 -0
  57. data/MAINTAINING.md +301 -0
  58. data/README.md +582 -28
  59. data/docs/agent_capabilities_api.md +259 -0
  60. data/docs/artifact_extension_points.md +757 -0
  61. data/docs/artifact_generation_architecture.md +323 -0
  62. data/docs/artifact_implementation_plan.md +596 -0
  63. data/docs/artifact_integration_points.md +345 -0
  64. data/docs/artifact_verification_strategies.md +581 -0
  65. data/docs/streaming_observability_architecture.md +510 -0
  66. data/exe/agentic +6 -1
  67. data/lefthook.yml +5 -0
  68. data/lib/agentic/adaptation_engine.rb +124 -0
  69. data/lib/agentic/agent.rb +181 -4
  70. data/lib/agentic/agent_assembly_engine.rb +442 -0
  71. data/lib/agentic/agent_capability_registry.rb +260 -0
  72. data/lib/agentic/agent_config.rb +63 -0
  73. data/lib/agentic/agent_specification.rb +46 -0
  74. data/lib/agentic/capabilities/examples.rb +530 -0
  75. data/lib/agentic/capabilities.rb +14 -0
  76. data/lib/agentic/capability_provider.rb +146 -0
  77. data/lib/agentic/capability_specification.rb +118 -0
  78. data/lib/agentic/cli/agent.rb +31 -0
  79. data/lib/agentic/cli/capabilities.rb +191 -0
  80. data/lib/agentic/cli/config.rb +134 -0
  81. data/lib/agentic/cli/execution_observer.rb +796 -0
  82. data/lib/agentic/cli.rb +1068 -0
  83. data/lib/agentic/default_agent_provider.rb +35 -0
  84. data/lib/agentic/errors/llm_error.rb +184 -0
  85. data/lib/agentic/execution_plan.rb +53 -0
  86. data/lib/agentic/execution_result.rb +91 -0
  87. data/lib/agentic/expected_answer_format.rb +46 -0
  88. data/lib/agentic/extension/domain_adapter.rb +109 -0
  89. data/lib/agentic/extension/plugin_manager.rb +163 -0
  90. data/lib/agentic/extension/protocol_handler.rb +116 -0
  91. data/lib/agentic/extension.rb +45 -0
  92. data/lib/agentic/factory_methods.rb +9 -1
  93. data/lib/agentic/generation_stats.rb +61 -0
  94. data/lib/agentic/learning/README.md +84 -0
  95. data/lib/agentic/learning/capability_optimizer.rb +613 -0
  96. data/lib/agentic/learning/execution_history_store.rb +251 -0
  97. data/lib/agentic/learning/pattern_recognizer.rb +500 -0
  98. data/lib/agentic/learning/strategy_optimizer.rb +706 -0
  99. data/lib/agentic/learning.rb +131 -0
  100. data/lib/agentic/llm_assisted_composition_strategy.rb +188 -0
  101. data/lib/agentic/llm_client.rb +215 -15
  102. data/lib/agentic/llm_config.rb +65 -1
  103. data/lib/agentic/llm_response.rb +163 -0
  104. data/lib/agentic/logger.rb +1 -1
  105. data/lib/agentic/observable.rb +51 -0
  106. data/lib/agentic/persistent_agent_store.rb +385 -0
  107. data/lib/agentic/plan_execution_result.rb +129 -0
  108. data/lib/agentic/plan_orchestrator.rb +464 -0
  109. data/lib/agentic/plan_orchestrator_config.rb +57 -0
  110. data/lib/agentic/retry_config.rb +63 -0
  111. data/lib/agentic/retry_handler.rb +125 -0
  112. data/lib/agentic/structured_outputs.rb +1 -1
  113. data/lib/agentic/task.rb +193 -0
  114. data/lib/agentic/task_definition.rb +39 -0
  115. data/lib/agentic/task_execution_result.rb +92 -0
  116. data/lib/agentic/task_failure.rb +66 -0
  117. data/lib/agentic/task_output_schemas.rb +112 -0
  118. data/lib/agentic/task_planner.rb +54 -19
  119. data/lib/agentic/task_result.rb +48 -0
  120. data/lib/agentic/ui.rb +244 -0
  121. data/lib/agentic/verification/critic_framework.rb +116 -0
  122. data/lib/agentic/verification/llm_verification_strategy.rb +60 -0
  123. data/lib/agentic/verification/schema_verification_strategy.rb +47 -0
  124. data/lib/agentic/verification/verification_hub.rb +62 -0
  125. data/lib/agentic/verification/verification_result.rb +50 -0
  126. data/lib/agentic/verification/verification_strategy.rb +26 -0
  127. data/lib/agentic/version.rb +1 -1
  128. data/lib/agentic.rb +74 -2
  129. data/plugins/README.md +41 -0
  130. metadata +245 -6
@@ -0,0 +1,530 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../capability_specification"
4
+ require_relative "../capability_provider"
5
+ require_relative "../agent_capability_registry"
6
+
7
+ module Agentic
8
+ module Capabilities
9
+ # Example capabilities for common tasks
10
+ module Examples
11
+ class << self
12
+ # Register all example capabilities
13
+ # @return [void]
14
+ def register_all
15
+ register_text_generation
16
+ register_web_search
17
+ register_data_analysis
18
+ register_code_generation
19
+ register_summarization
20
+ register_brainstorming
21
+ register_structured_extraction
22
+ end
23
+
24
+ # Register a text generation capability
25
+ # @return [CapabilitySpecification] The registered capability
26
+ def register_text_generation
27
+ spec = CapabilitySpecification.new(
28
+ name: "text_generation",
29
+ description: "Generates text based on a prompt",
30
+ version: "1.0.0",
31
+ inputs: {
32
+ prompt: {
33
+ type: "string",
34
+ required: true,
35
+ description: "The prompt to generate text from"
36
+ },
37
+ max_tokens: {
38
+ type: "integer",
39
+ description: "Maximum number of tokens to generate"
40
+ },
41
+ temperature: {
42
+ type: "number",
43
+ description: "Sampling temperature (0.0-1.0)"
44
+ }
45
+ },
46
+ outputs: {
47
+ response: {
48
+ type: "string",
49
+ required: true,
50
+ description: "The generated text"
51
+ }
52
+ }
53
+ )
54
+
55
+ provider = CapabilityProvider.new(
56
+ capability: spec,
57
+ implementation: lambda do |inputs|
58
+ # Get the LLM client
59
+ llm_config = LlmConfig.new
60
+ llm_config.max_tokens = inputs[:max_tokens] if inputs[:max_tokens]
61
+ llm_config.temperature = inputs[:temperature] if inputs[:temperature]
62
+
63
+ client = Agentic.client(llm_config)
64
+
65
+ # Generate text
66
+ response = client.complete(prompt: inputs[:prompt])
67
+
68
+ {response: response.to_s}
69
+ end
70
+ )
71
+
72
+ registry.register(spec, provider)
73
+ end
74
+
75
+ # Register a web search capability
76
+ # @return [CapabilitySpecification] The registered capability
77
+ def register_web_search
78
+ spec = CapabilitySpecification.new(
79
+ name: "web_search",
80
+ description: "Searches the web for information",
81
+ version: "1.0.0",
82
+ inputs: {
83
+ query: {
84
+ type: "string",
85
+ required: true,
86
+ description: "The search query"
87
+ },
88
+ num_results: {
89
+ type: "integer",
90
+ description: "Number of results to return"
91
+ }
92
+ },
93
+ outputs: {
94
+ results: {
95
+ type: "array",
96
+ required: true,
97
+ description: "The search results"
98
+ },
99
+ sources: {
100
+ type: "array",
101
+ description: "The sources of the results"
102
+ }
103
+ }
104
+ )
105
+
106
+ provider = CapabilityProvider.new(
107
+ capability: spec,
108
+ implementation: lambda do |inputs|
109
+ # This is a mock implementation
110
+ # In a real implementation, you would use a search API or web scraping
111
+
112
+ query = inputs[:query]
113
+ num_results = inputs[:num_results] || 3
114
+
115
+ results = num_results.times.map do |i|
116
+ "Result #{i + 1} for query: #{query}"
117
+ end
118
+
119
+ sources = num_results.times.map do |i|
120
+ "https://example.com/result#{i + 1}"
121
+ end
122
+
123
+ {
124
+ results: results,
125
+ sources: sources
126
+ }
127
+ end
128
+ )
129
+
130
+ registry.register(spec, provider)
131
+ end
132
+
133
+ # Register a data analysis capability
134
+ # @return [CapabilitySpecification] The registered capability
135
+ def register_data_analysis
136
+ spec = CapabilitySpecification.new(
137
+ name: "data_analysis",
138
+ description: "Analyzes data and extracts insights",
139
+ version: "1.0.0",
140
+ inputs: {
141
+ data: {
142
+ type: "object",
143
+ required: true,
144
+ description: "The data to analyze"
145
+ },
146
+ analysis_type: {
147
+ type: "string",
148
+ description: "The type of analysis to perform"
149
+ }
150
+ },
151
+ outputs: {
152
+ insights: {
153
+ type: "array",
154
+ required: true,
155
+ description: "The extracted insights"
156
+ },
157
+ summary: {
158
+ type: "string",
159
+ required: true,
160
+ description: "A summary of the analysis"
161
+ }
162
+ },
163
+ dependencies: [
164
+ {name: "text_generation", version: "1.0.0"}
165
+ ]
166
+ )
167
+
168
+ provider = CapabilityProvider.new(
169
+ capability: spec,
170
+ implementation: lambda do |inputs|
171
+ data = inputs[:data]
172
+ analysis_type = inputs[:analysis_type] || "basic"
173
+
174
+ # Get the text generation capability
175
+ text_gen_provider = registry.get_provider("text_generation")
176
+
177
+ # Generate insights based on the data
178
+ insights_prompt = "Analyze the following data using #{analysis_type} analysis and provide key insights:\n\n#{data.inspect}"
179
+ insights_response = text_gen_provider.execute(prompt: insights_prompt)[:response]
180
+
181
+ # Parse insights
182
+ insights = insights_response.split("\n").map(&:strip).reject(&:empty?)
183
+
184
+ # Generate summary
185
+ summary_prompt = "Summarize the following insights in one paragraph:\n\n#{insights.join("\n")}"
186
+ summary = text_gen_provider.execute(prompt: summary_prompt)[:response]
187
+
188
+ {
189
+ insights: insights,
190
+ summary: summary
191
+ }
192
+ end
193
+ )
194
+
195
+ registry.register(spec, provider)
196
+ end
197
+
198
+ # Register a code generation capability
199
+ # @return [CapabilitySpecification] The registered capability
200
+ def register_code_generation
201
+ spec = CapabilitySpecification.new(
202
+ name: "code_generation",
203
+ description: "Generates code based on requirements",
204
+ version: "1.0.0",
205
+ inputs: {
206
+ requirements: {
207
+ type: "string",
208
+ required: true,
209
+ description: "The code requirements"
210
+ },
211
+ language: {
212
+ type: "string",
213
+ required: true,
214
+ description: "The programming language"
215
+ },
216
+ include_comments: {
217
+ type: "boolean",
218
+ description: "Whether to include comments in the code"
219
+ }
220
+ },
221
+ outputs: {
222
+ code: {
223
+ type: "string",
224
+ required: true,
225
+ description: "The generated code"
226
+ },
227
+ explanation: {
228
+ type: "string",
229
+ description: "Explanation of the code"
230
+ }
231
+ },
232
+ dependencies: [
233
+ {name: "text_generation", version: "1.0.0"}
234
+ ]
235
+ )
236
+
237
+ provider = CapabilityProvider.new(
238
+ capability: spec,
239
+ implementation: lambda do |inputs|
240
+ requirements = inputs[:requirements]
241
+ language = inputs[:language]
242
+ include_comments = inputs[:include_comments] || false
243
+
244
+ # Get the text generation capability
245
+ text_gen_provider = registry.get_provider("text_generation")
246
+
247
+ # Generate code
248
+ comments_instruction = include_comments ? "Include detailed comments." : "Keep comments minimal."
249
+ code_prompt = "Generate #{language} code for the following requirements:\n\n#{requirements}\n\n#{comments_instruction}"
250
+ code = text_gen_provider.execute(prompt: code_prompt)[:response]
251
+
252
+ # Generate explanation if needed
253
+ explanation = nil
254
+ if include_comments
255
+ explanation_prompt = "Explain the following #{language} code:\n\n#{code}"
256
+ explanation = text_gen_provider.execute(prompt: explanation_prompt)[:response]
257
+ end
258
+
259
+ result = {code: code}
260
+ result[:explanation] = explanation if explanation
261
+
262
+ result
263
+ end
264
+ )
265
+
266
+ registry.register(spec, provider)
267
+ end
268
+
269
+ # Register a summarization capability
270
+ # @return [CapabilitySpecification] The registered capability
271
+ def register_summarization
272
+ spec = CapabilitySpecification.new(
273
+ name: "summarization",
274
+ description: "Summarizes text content",
275
+ version: "1.0.0",
276
+ inputs: {
277
+ content: {
278
+ type: "string",
279
+ required: true,
280
+ description: "The content to summarize"
281
+ },
282
+ max_length: {
283
+ type: "integer",
284
+ description: "Maximum length of the summary"
285
+ },
286
+ format: {
287
+ type: "string",
288
+ description: "Format of the summary (paragraph, bullets, etc.)"
289
+ }
290
+ },
291
+ outputs: {
292
+ summary: {
293
+ type: "string",
294
+ required: true,
295
+ description: "The generated summary"
296
+ },
297
+ key_points: {
298
+ type: "array",
299
+ description: "Key points from the content"
300
+ }
301
+ },
302
+ dependencies: [
303
+ {name: "text_generation", version: "1.0.0"}
304
+ ]
305
+ )
306
+
307
+ provider = CapabilityProvider.new(
308
+ capability: spec,
309
+ implementation: lambda do |inputs|
310
+ content = inputs[:content]
311
+ max_length = inputs[:max_length]
312
+ format = inputs[:format] || "paragraph"
313
+
314
+ # Get the text generation capability
315
+ text_gen_provider = registry.get_provider("text_generation")
316
+
317
+ # Generate summary
318
+ length_instruction = max_length ? "Keep the summary under #{max_length} words." : ""
319
+ summary_prompt = "Summarize the following content in #{format} format. #{length_instruction}\n\n#{content}"
320
+ summary = text_gen_provider.execute(prompt: summary_prompt)[:response]
321
+
322
+ # Extract key points
323
+ key_points_prompt = "Extract 3-5 key points from the following content:\n\n#{content}"
324
+ key_points_response = text_gen_provider.execute(prompt: key_points_prompt)[:response]
325
+ key_points = key_points_response.split("\n").map(&:strip).reject(&:empty?)
326
+
327
+ {
328
+ summary: summary,
329
+ key_points: key_points
330
+ }
331
+ end
332
+ )
333
+
334
+ registry.register(spec, provider)
335
+ end
336
+
337
+ # Register a brainstorming capability
338
+ # @return [CapabilitySpecification] The registered capability
339
+ def register_brainstorming
340
+ spec = CapabilitySpecification.new(
341
+ name: "brainstorming",
342
+ description: "Generates creative ideas for a topic",
343
+ version: "1.0.0",
344
+ inputs: {
345
+ topic: {
346
+ type: "string",
347
+ required: true,
348
+ description: "The topic to brainstorm about"
349
+ },
350
+ num_ideas: {
351
+ type: "integer",
352
+ description: "Number of ideas to generate"
353
+ },
354
+ creativity: {
355
+ type: "number",
356
+ description: "Creativity level (0.0-1.0)"
357
+ }
358
+ },
359
+ outputs: {
360
+ ideas: {
361
+ type: "array",
362
+ required: true,
363
+ description: "The generated ideas"
364
+ },
365
+ themes: {
366
+ type: "array",
367
+ description: "Common themes across the ideas"
368
+ }
369
+ },
370
+ dependencies: [
371
+ {name: "text_generation", version: "1.0.0"}
372
+ ]
373
+ )
374
+
375
+ provider = CapabilityProvider.new(
376
+ capability: spec,
377
+ implementation: lambda do |inputs|
378
+ topic = inputs[:topic]
379
+ num_ideas = inputs[:num_ideas] || 10
380
+ creativity = inputs[:creativity] || 0.7
381
+
382
+ # Get the text generation capability
383
+ text_gen_provider = registry.get_provider("text_generation")
384
+
385
+ # Generate ideas
386
+ ideas_prompt = "Brainstorm #{num_ideas} creative ideas about: #{topic}"
387
+ ideas_response = text_gen_provider.execute(
388
+ prompt: ideas_prompt,
389
+ temperature: creativity
390
+ )[:response]
391
+
392
+ # Parse ideas
393
+ ideas = ideas_response.split("\n").map { |line| line.sub(/^\d+\.\s*/, "") }.map(&:strip).reject(&:empty?)
394
+
395
+ # Identify themes
396
+ themes_prompt = "Identify 3-5 common themes in the following ideas:\n\n#{ideas.join("\n")}"
397
+ themes_response = text_gen_provider.execute(prompt: themes_prompt)[:response]
398
+ themes = themes_response.split("\n").map(&:strip).reject(&:empty?)
399
+
400
+ {
401
+ ideas: ideas,
402
+ themes: themes
403
+ }
404
+ end
405
+ )
406
+
407
+ registry.register(spec, provider)
408
+ end
409
+
410
+ # Register a structured extraction capability
411
+ # @return [CapabilitySpecification] The registered capability
412
+ def register_structured_extraction
413
+ spec = CapabilitySpecification.new(
414
+ name: "structured_extraction",
415
+ description: "Extracts structured data from text",
416
+ version: "1.0.0",
417
+ inputs: {
418
+ content: {
419
+ type: "string",
420
+ required: true,
421
+ description: "The content to extract from"
422
+ },
423
+ schema: {
424
+ type: "object",
425
+ required: true,
426
+ description: "The schema to extract"
427
+ }
428
+ },
429
+ outputs: {
430
+ data: {
431
+ type: "object",
432
+ required: true,
433
+ description: "The extracted data"
434
+ },
435
+ confidence: {
436
+ type: "number",
437
+ description: "Confidence score for the extraction"
438
+ }
439
+ },
440
+ dependencies: [
441
+ {name: "text_generation", version: "1.0.0"}
442
+ ]
443
+ )
444
+
445
+ provider = CapabilityProvider.new(
446
+ capability: spec,
447
+ implementation: lambda do |inputs|
448
+ content = inputs[:content]
449
+ schema = inputs[:schema]
450
+
451
+ # Get the text generation capability
452
+ text_gen_provider = registry.get_provider("text_generation")
453
+
454
+ # Create extraction prompt
455
+ schema_str = JSON.pretty_generate(schema)
456
+ extraction_prompt = <<-PROMPT
457
+ Extract structured data from the following content according to this schema:
458
+
459
+ #{schema_str}
460
+
461
+ Content:
462
+ #{content}
463
+
464
+ Return the data as valid JSON.
465
+ PROMPT
466
+
467
+ # Generate extraction
468
+ extraction_response = text_gen_provider.execute(prompt: extraction_prompt)[:response]
469
+
470
+ # Parse JSON (with error handling)
471
+ data = nil
472
+ begin
473
+ # Clean the response to ensure it's valid JSON
474
+ json_str = extraction_response.gsub(/```json|```/, "").strip
475
+ data = JSON.parse(json_str)
476
+ rescue JSON::ParserError
477
+ # Fallback extraction for malformed JSON
478
+ data = extract_fallback(extraction_response)
479
+ end
480
+
481
+ # Calculate confidence based on schema match
482
+ confidence = calculate_confidence(data, schema)
483
+
484
+ {
485
+ data: data,
486
+ confidence: confidence
487
+ }
488
+ end
489
+ )
490
+
491
+ registry.register(spec, provider)
492
+ end
493
+
494
+ private
495
+
496
+ def registry
497
+ AgentCapabilityRegistry.instance
498
+ end
499
+
500
+ # Fallback extraction for malformed JSON
501
+ def extract_fallback(text)
502
+ result = {}
503
+
504
+ # Try to extract key-value pairs
505
+ text.scan(/["']?([^"':]+)["']?\s*:\s*["']?([^"',}]+)["']?/) do |key, value|
506
+ result[key.strip] = value.strip
507
+ end
508
+
509
+ result
510
+ end
511
+
512
+ # Calculate confidence based on schema match
513
+ def calculate_confidence(data, schema)
514
+ return 0.0 if data.nil?
515
+
516
+ # Count matching fields
517
+ matches = 0
518
+ total = 0
519
+
520
+ schema.each do |key, _|
521
+ total += 1
522
+ matches += 1 if data.key?(key.to_s) || data.key?(key.to_sym)
523
+ end
524
+
525
+ (total > 0) ? matches.to_f / total : 0.0
526
+ end
527
+ end
528
+ end
529
+ end
530
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "capabilities/examples"
4
+
5
+ module Agentic
6
+ # Namespace for capability-related functionality
7
+ module Capabilities
8
+ # Register standard capabilities
9
+ # @return [void]
10
+ def self.register_standard_capabilities
11
+ Examples.register_all
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ # Provider for a capability implementation
5
+ # @attr_reader [CapabilitySpecification] capability The capability specification
6
+ # @attr_reader [Proc, Class] implementation The implementation of the capability
7
+ class CapabilityProvider
8
+ attr_reader :capability, :implementation
9
+
10
+ # Initialize a new capability provider
11
+ # @param capability [CapabilitySpecification] The capability specification
12
+ # @param implementation [Proc, Class] The implementation of the capability
13
+ def initialize(capability:, implementation:)
14
+ @capability = capability
15
+ @implementation = implementation
16
+ end
17
+
18
+ # Execute the capability
19
+ # @param inputs [Hash] The inputs for the capability
20
+ # @return [Hash] The outputs from the capability
21
+ def execute(inputs = {})
22
+ # Validate inputs against capability specification
23
+ validate_inputs!(inputs)
24
+
25
+ # Execute the implementation
26
+ result = case @implementation
27
+ when Proc
28
+ @implementation.call(inputs)
29
+ when Class
30
+ instance = @implementation.new
31
+ instance.execute(inputs)
32
+ else
33
+ raise "Invalid implementation type: #{@implementation.class}"
34
+ end
35
+
36
+ # Validate outputs against capability specification
37
+ validate_outputs!(result)
38
+
39
+ result
40
+ end
41
+
42
+ private
43
+
44
+ def validate_inputs!(inputs)
45
+ # Skip validation if there are no input specifications
46
+ return unless @capability.inputs && !@capability.inputs.empty?
47
+
48
+ # Check for required inputs
49
+ @capability.inputs.each do |name, spec|
50
+ if spec[:required] && !inputs.key?(name.to_sym) && !inputs.key?(name.to_s)
51
+ raise "Missing required input: #{name}"
52
+ end
53
+ end
54
+
55
+ # Validate input types (if specified)
56
+ inputs.each do |name, value|
57
+ name_sym = name.to_sym
58
+ name_str = name.to_s
59
+
60
+ # Skip inputs that aren't in the specification
61
+ next unless @capability.inputs.key?(name_sym) || @capability.inputs.key?(name_str)
62
+
63
+ # Get the spec for this input
64
+ input_spec = @capability.inputs[name_sym] || @capability.inputs[name_str]
65
+
66
+ # Skip if no type is specified
67
+ next unless input_spec[:type]
68
+
69
+ # Check type
70
+ case input_spec[:type]
71
+ when "string"
72
+ unless value.is_a?(String)
73
+ raise "Input #{name} must be a string"
74
+ end
75
+ when "number", "integer"
76
+ unless value.is_a?(Numeric)
77
+ raise "Input #{name} must be a number"
78
+ end
79
+ when "boolean"
80
+ unless value == true || value == false
81
+ raise "Input #{name} must be a boolean"
82
+ end
83
+ when "array"
84
+ unless value.is_a?(Array)
85
+ raise "Input #{name} must be an array"
86
+ end
87
+ when "object", "hash"
88
+ unless value.is_a?(Hash)
89
+ raise "Input #{name} must be an object/hash"
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def validate_outputs!(outputs)
96
+ # Skip validation if there are no output specifications or the output is nil
97
+ return unless @capability.outputs && !@capability.outputs.empty? && outputs
98
+
99
+ # Check for required outputs
100
+ @capability.outputs.each do |name, spec|
101
+ if spec[:required] && !outputs.key?(name.to_sym) && !outputs.key?(name.to_s)
102
+ raise "Missing required output: #{name}"
103
+ end
104
+ end
105
+
106
+ # Validate output types (if specified)
107
+ outputs.each do |name, value|
108
+ name_sym = name.to_sym
109
+ name_str = name.to_s
110
+
111
+ # Skip outputs that aren't in the specification
112
+ next unless @capability.outputs.key?(name_sym) || @capability.outputs.key?(name_str)
113
+
114
+ # Get the spec for this output
115
+ output_spec = @capability.outputs[name_sym] || @capability.outputs[name_str]
116
+
117
+ # Skip if no type is specified
118
+ next unless output_spec[:type]
119
+
120
+ # Check type
121
+ case output_spec[:type]
122
+ when "string"
123
+ unless value.is_a?(String)
124
+ raise "Output #{name} must be a string"
125
+ end
126
+ when "number", "integer"
127
+ unless value.is_a?(Numeric)
128
+ raise "Output #{name} must be a number"
129
+ end
130
+ when "boolean"
131
+ unless value == true || value == false
132
+ raise "Output #{name} must be a boolean"
133
+ end
134
+ when "array"
135
+ unless value.is_a?(Array)
136
+ raise "Output #{name} must be an array"
137
+ end
138
+ when "object", "hash"
139
+ unless value.is_a?(Hash)
140
+ raise "Output #{name} must be an object/hash"
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end