phronomy 0.27.0 → 0.28.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.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +9 -3
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +8 -3
- data/docs/getting-started.md +9 -6
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +37 -32
- data/lib/phronomy/agent/base.rb +74 -72
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +8 -2
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/tool/tool_executor.rb +58 -0
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/workflow/execution/workflow.rb +7 -7
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
- data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +57 -16
- data/scripts/api_snapshot.rb +35 -15
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -138
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +6 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +1 -99
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
- data/sig/phronomy/tool.rbs +48 -4
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +318 -117
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
data/lib/phronomy/tool/base.rb
CHANGED
|
@@ -1,15 +1,555 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../execution/concurrency/worker_input_restricted"
|
|
4
|
+
require_relative "../execution/cancellation_error"
|
|
5
|
+
require_relative "../configuration/runtime_settings"
|
|
6
|
+
require_relative "settings"
|
|
7
|
+
require_relative "tool_error"
|
|
8
|
+
require_relative "schema"
|
|
9
|
+
|
|
3
10
|
module Phronomy
|
|
4
11
|
module Tool
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
# Owns Tool declarations, argument validation and the execution template.
|
|
13
|
+
class Base
|
|
14
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
15
|
+
|
|
16
|
+
Parameter = Data.define(:name, :type, :description, :required)
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# @api public
|
|
20
|
+
def parameter(name, type: :string, description: nil, required: true)
|
|
21
|
+
declared_parameters[name.to_sym] = Parameter.new(name: name.to_sym,
|
|
22
|
+
type: type, description: description, required: required)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @api public
|
|
26
|
+
def tool_name(value = nil)
|
|
27
|
+
if value.nil?
|
|
28
|
+
return @tool_name if instance_variable_defined?(:@tool_name)
|
|
29
|
+
ascii = name.to_s.unicode_normalize(:nfkd).encode("ASCII", replace: "").gsub(/[^a-zA-Z0-9_-]/, "-")
|
|
30
|
+
return ascii.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.delete_suffix("_tool")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@tool_name = value.to_s
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Tool descriptions inherit through application and decorator classes.
|
|
37
|
+
# Preserve normal class inheritance semantics so Phronomy's anonymous
|
|
38
|
+
# decorator subclasses do not lose their parent's description.
|
|
39
|
+
# @api public
|
|
40
|
+
def description(text = nil)
|
|
41
|
+
unless text
|
|
42
|
+
return @description if instance_variable_defined?(:@description)
|
|
43
|
+
return superclass.description if superclass.respond_to?(:description)
|
|
44
|
+
|
|
45
|
+
return nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
@description = text
|
|
49
|
+
end
|
|
50
|
+
alias_method :desc, :description
|
|
51
|
+
|
|
52
|
+
# Phronomy declarations inherit through anonymous Tool decorators.
|
|
53
|
+
# The Tool contract owns schema construction and validation.
|
|
54
|
+
# @api public
|
|
55
|
+
def declared_parameters
|
|
56
|
+
return @declared_parameters if instance_variable_defined?(:@declared_parameters)
|
|
57
|
+
|
|
58
|
+
@declared_parameters = superclass.respond_to?(:declared_parameters) ? superclass.declared_parameters.dup : {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Preserve the parameter reader and accept an explicit schema or schema DSL.
|
|
62
|
+
# @api public
|
|
63
|
+
def parameters(schema = :__phronomy_read__, &block)
|
|
64
|
+
return declared_parameters if schema == :__phronomy_read__ && !block
|
|
65
|
+
|
|
66
|
+
@parameters_schema_definition = Schema.new((schema == :__phronomy_read__) ? nil : schema, &block)
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @api public
|
|
71
|
+
def parameters_schema_definition
|
|
72
|
+
return @parameters_schema_definition if instance_variable_defined?(:@parameters_schema_definition)
|
|
73
|
+
|
|
74
|
+
superclass.parameters_schema_definition if superclass.respond_to?(:parameters_schema_definition)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @api public
|
|
78
|
+
def params_schema_definition
|
|
79
|
+
parameters_schema_definition
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @api public
|
|
83
|
+
def params(schema = nil, &block)
|
|
84
|
+
parameters(schema, &block)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @api public
|
|
88
|
+
def provider_options(options = :__phronomy_read__)
|
|
89
|
+
unless options == :__phronomy_read__
|
|
90
|
+
raise ArgumentError, "provider_options must be a Hash" unless options.is_a?(Hash)
|
|
91
|
+
@provider_options = duplicate_configuration(options)
|
|
92
|
+
return self
|
|
93
|
+
end
|
|
94
|
+
return @provider_options if instance_variable_defined?(:@provider_options)
|
|
95
|
+
|
|
96
|
+
@provider_options = superclass.respond_to?(:provider_options) ? duplicate_configuration(superclass.provider_options) : {}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @api public
|
|
100
|
+
def provider_params
|
|
101
|
+
provider_options
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @api public
|
|
105
|
+
def with_params(**options)
|
|
106
|
+
provider_options(options)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @api public
|
|
110
|
+
def param(name, enum: nil, properties: nil, desc: nil, **options)
|
|
111
|
+
options[:description] = desc if desc
|
|
112
|
+
parameter(name, **options)
|
|
113
|
+
param_enums[name] = duplicate_configuration(enum) if enum
|
|
114
|
+
param_schemas[name] = normalize_nested_schema(properties) if properties
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @api public
|
|
118
|
+
def param_enums
|
|
119
|
+
return @param_enums if instance_variable_defined?(:@param_enums)
|
|
120
|
+
|
|
121
|
+
parent = superclass.respond_to?(:param_enums) ? superclass.param_enums : {}
|
|
122
|
+
@param_enums = duplicate_configuration(parent)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @api public
|
|
126
|
+
def param_schemas
|
|
127
|
+
return @param_schemas if instance_variable_defined?(:@param_schemas)
|
|
128
|
+
|
|
129
|
+
parent = superclass.respond_to?(:param_schemas) ? superclass.param_schemas : {}
|
|
130
|
+
@param_schemas = duplicate_configuration(parent)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def duplicate_configuration(value)
|
|
136
|
+
case value
|
|
137
|
+
when Hash
|
|
138
|
+
value.to_h do |key, child|
|
|
139
|
+
[key, duplicate_configuration(child)]
|
|
140
|
+
end
|
|
141
|
+
when Array
|
|
142
|
+
value.map { |child| duplicate_configuration(child) }
|
|
143
|
+
else
|
|
144
|
+
value
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def normalize_nested_schema(props)
|
|
149
|
+
props.transform_keys(&:to_sym).transform_values do |spec|
|
|
150
|
+
normalized = spec.transform_keys(&:to_sym)
|
|
151
|
+
normalized[:type] ||= :string
|
|
152
|
+
if normalized[:properties]
|
|
153
|
+
normalized[:properties] = normalize_nested_schema(normalized[:properties])
|
|
154
|
+
end
|
|
155
|
+
normalized
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
public
|
|
160
|
+
|
|
161
|
+
# Declares whether Tool work is safe to run cooperatively on the EventLoop
|
|
162
|
+
# or must be offloaded to a bounded worker pool.
|
|
163
|
+
#
|
|
164
|
+
# Phronomy does not classify the reason for offloading. Blocking I/O,
|
|
165
|
+
# CPU-bound synchronous work, and other long synchronous calls all use
|
|
166
|
+
# +:offloaded+. The application owns that workload classification.
|
|
167
|
+
# @api public
|
|
168
|
+
def execution_mode(value = nil)
|
|
169
|
+
if value.nil?
|
|
170
|
+
return @execution_mode if instance_variable_defined?(:@execution_mode)
|
|
171
|
+
return superclass.execution_mode if superclass.respond_to?(:execution_mode)
|
|
172
|
+
|
|
173
|
+
return :offloaded
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
valid = %i[cooperative offloaded]
|
|
177
|
+
unless valid.include?(value)
|
|
178
|
+
raise ArgumentError,
|
|
179
|
+
"execution_mode must be one of #{valid.inspect}, got #{value.inspect}"
|
|
180
|
+
end
|
|
181
|
+
@execution_mode = value
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Configures execution-error handling. Supported values are :raise
|
|
185
|
+
# and :suppress only.
|
|
186
|
+
# @api public
|
|
187
|
+
def on_error(behavior = nil)
|
|
188
|
+
if behavior.nil?
|
|
189
|
+
return @on_error if instance_variable_defined?(:@on_error)
|
|
190
|
+
return superclass.on_error if superclass.respond_to?(:on_error)
|
|
191
|
+
|
|
192
|
+
return :raise
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
valid = %i[raise suppress]
|
|
196
|
+
unless valid.include?(behavior)
|
|
197
|
+
raise ArgumentError,
|
|
198
|
+
"on_error must be one of #{valid.inspect}, got #{behavior.inspect}"
|
|
199
|
+
end
|
|
200
|
+
@on_error = behavior
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @api public
|
|
204
|
+
def on_schema_error(behavior = nil)
|
|
205
|
+
if behavior.nil?
|
|
206
|
+
return @on_schema_error if instance_variable_defined?(:@on_schema_error)
|
|
207
|
+
return superclass.on_schema_error if superclass.respond_to?(:on_schema_error)
|
|
208
|
+
|
|
209
|
+
return :return_error
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
@on_schema_error = behavior
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# @api public
|
|
216
|
+
def requires_approval(value = :__unset__, &block)
|
|
217
|
+
if block
|
|
218
|
+
unless value == :__unset__
|
|
219
|
+
raise ArgumentError, "pass either a value or a block to requires_approval"
|
|
220
|
+
end
|
|
221
|
+
@requires_approval = block
|
|
222
|
+
elsif value == :__unset__
|
|
223
|
+
return @requires_approval unless @requires_approval.nil?
|
|
224
|
+
return superclass.requires_approval if superclass.respond_to?(:requires_approval)
|
|
225
|
+
|
|
226
|
+
false
|
|
227
|
+
else
|
|
228
|
+
unless value == true || value == false || value.respond_to?(:call)
|
|
229
|
+
raise ArgumentError, "requires_approval must be true, false, or callable"
|
|
230
|
+
end
|
|
231
|
+
@requires_approval = value
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# @api public
|
|
236
|
+
def approval_facts(&block)
|
|
237
|
+
if block
|
|
238
|
+
@approval_facts = block
|
|
239
|
+
elsif instance_variable_defined?(:@approval_facts)
|
|
240
|
+
@approval_facts
|
|
241
|
+
elsif superclass.respond_to?(:approval_facts)
|
|
242
|
+
superclass.approval_facts
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# @api public
|
|
247
|
+
def redact_params(*names)
|
|
248
|
+
if names.empty?
|
|
249
|
+
parent = superclass.respond_to?(:redact_params) ? superclass.redact_params : []
|
|
250
|
+
((@redacted_params || []) + parent).uniq
|
|
251
|
+
else
|
|
252
|
+
@redacted_params = ((@redacted_params || []) + names.map(&:to_sym)).uniq
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# @api public
|
|
257
|
+
def max_result_size(value = :__unset__)
|
|
258
|
+
if value == :__unset__
|
|
259
|
+
return @max_result_size if instance_variable_defined?(:@max_result_size)
|
|
260
|
+
return superclass.max_result_size if superclass.respond_to?(:max_result_size)
|
|
261
|
+
|
|
262
|
+
return nil
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
@max_result_size = value
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def name
|
|
270
|
+
self.class.tool_name
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def description
|
|
274
|
+
self.class.description
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def provider_options
|
|
278
|
+
self.class.provider_options
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def parameters_schema
|
|
282
|
+
definition = self.class.parameters_schema_definition
|
|
283
|
+
return definition.json_schema if definition
|
|
284
|
+
|
|
285
|
+
declarations = self.class.declared_parameters
|
|
286
|
+
if declarations.empty?
|
|
287
|
+
declarations = method(:execute).parameters.filter_map do |kind, name|
|
|
288
|
+
next unless %i[key keyreq].include?(kind)
|
|
289
|
+
next if %i[cancellation_token tool_call].include?(name)
|
|
290
|
+
[name, Parameter.new(name: name, type: :string, description: nil, required: kind == :keyreq)]
|
|
291
|
+
end.to_h
|
|
292
|
+
end
|
|
293
|
+
properties = declarations.to_h do |name, parameter|
|
|
294
|
+
type = parameter.type.to_s
|
|
295
|
+
type = "number" if %w[float double].include?(type)
|
|
296
|
+
type = "integer" if type == "int"
|
|
297
|
+
value = {"type" => type, "description" => parameter.description}.compact
|
|
298
|
+
# An unqualified array declaration permits any JSON item. Explicit
|
|
299
|
+
# item constraints belong to an explicit schema.
|
|
300
|
+
value["items"] = {} if type == "array"
|
|
301
|
+
value["type"] = [type, "null"] unless parameter.required
|
|
302
|
+
[name.to_s, value]
|
|
303
|
+
end
|
|
304
|
+
schema = {"type" => "object", "properties" => properties,
|
|
305
|
+
"required" => declarations.select { |_, parameter| parameter.required }.keys.map(&:to_s),
|
|
306
|
+
"additionalProperties" => false}
|
|
307
|
+
|
|
308
|
+
properties = schema.dig("properties") || schema.dig(:properties)
|
|
309
|
+
return schema unless properties
|
|
310
|
+
|
|
311
|
+
self.class.param_enums.each do |param_name, values|
|
|
312
|
+
key = properties.key?(param_name.to_s) ? param_name.to_s : param_name.to_sym
|
|
313
|
+
next unless properties[key]
|
|
314
|
+
|
|
315
|
+
param_type = Array(properties[key]["type"]).find { |type| type != "null" }
|
|
316
|
+
properties[key]["enum"] = values.map do |value|
|
|
317
|
+
case param_type
|
|
318
|
+
when "integer"
|
|
319
|
+
value.is_a?(Integer) ? value : Integer(value.to_s)
|
|
320
|
+
when "number"
|
|
321
|
+
value.is_a?(Numeric) ? value : Float(value.to_s)
|
|
322
|
+
when "boolean"
|
|
323
|
+
unless value == true || value == false
|
|
324
|
+
raise ArgumentError,
|
|
325
|
+
"boolean enum values must be true or false (got: #{value.inspect})"
|
|
326
|
+
end
|
|
327
|
+
value
|
|
328
|
+
else
|
|
329
|
+
value.to_s
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
properties[key]["enum"] << nil unless declarations.fetch(param_name.to_sym).required
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
self.class.param_schemas.each do |param_name, nested|
|
|
336
|
+
key = properties.key?(param_name.to_s) ? param_name.to_s : param_name.to_sym
|
|
337
|
+
next unless properties[key]
|
|
338
|
+
properties[key]["properties"] = nested_schema_to_json_schema(nested)
|
|
339
|
+
properties[key]["required"] = nested.select { |_, spec| spec[:required] }.keys.map(&:to_s)
|
|
340
|
+
properties[key]["additionalProperties"] = false
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
schema
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Phronomy retains these readers as part of its Tool contract.
|
|
347
|
+
# @api public
|
|
348
|
+
def params_schema
|
|
349
|
+
parameters_schema
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# @api public
|
|
353
|
+
def provider_params
|
|
354
|
+
provider_options
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# @api public
|
|
358
|
+
def parameters
|
|
359
|
+
self.class.declared_parameters
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Validate/coerce without executing, so approval and execution share inputs.
|
|
363
|
+
# @api public
|
|
364
|
+
def validate_arguments(args)
|
|
365
|
+
schema = parameters_schema
|
|
366
|
+
if !@validation_schema || @validation_schema.json_schema != schema
|
|
367
|
+
@validation_schema = Schema.new(schema)
|
|
368
|
+
end
|
|
369
|
+
values, error = @validation_schema.validate(args || {}, coerce: self.class.on_schema_error == :coerce)
|
|
370
|
+
if values && !self.class.parameters_schema_definition
|
|
371
|
+
# Legacy optional param nil means omission, preserving Ruby defaults.
|
|
372
|
+
self.class.declared_parameters.each do |name, parameter|
|
|
373
|
+
values.delete(name) if !parameter.required && values[name].nil?
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
[values, error]
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# @api public
|
|
380
|
+
def call(args, cancellation_token: nil)
|
|
381
|
+
cancellation_token&.raise_if_cancelled!
|
|
382
|
+
validated_args, schema_error = validate_arguments(args)
|
|
383
|
+
if schema_error
|
|
384
|
+
case self.class.on_schema_error
|
|
385
|
+
when :raise
|
|
386
|
+
raise Phronomy::ToolError,
|
|
387
|
+
"#{self.class.name} schema error: #{schema_error}"
|
|
388
|
+
else
|
|
389
|
+
return "Schema validation failed: #{schema_error}"
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
if cancellation_token && execute_accepts_cancellation_token?
|
|
394
|
+
validated_args = validated_args.merge(cancellation_token: cancellation_token)
|
|
395
|
+
end
|
|
396
|
+
result = execute(**(validated_args || {}).transform_keys(&:to_sym))
|
|
397
|
+
truncate_result_if_needed(result)
|
|
398
|
+
rescue Phronomy::ToolError, Phronomy::CancellationError
|
|
399
|
+
raise
|
|
400
|
+
rescue => error
|
|
401
|
+
if self.class.on_error == :suppress
|
|
402
|
+
msg = "[Phronomy] Tool #{self.class.name} suppressed error: " \
|
|
403
|
+
"#{error.class}: #{error.message}"
|
|
404
|
+
if Phronomy::RuntimeSettings.current.logger
|
|
405
|
+
Phronomy::RuntimeSettings.current.logger.warn(msg)
|
|
406
|
+
else
|
|
407
|
+
warn msg
|
|
408
|
+
end
|
|
409
|
+
"Tool error suppressed: #{error.message}"
|
|
410
|
+
else
|
|
411
|
+
raise Phronomy::ToolError,
|
|
412
|
+
"#{self.class.name} execution failed: #{error.message}"
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# @api public
|
|
417
|
+
def call_async(
|
|
418
|
+
args,
|
|
419
|
+
cancellation_token: nil,
|
|
420
|
+
config: {}
|
|
421
|
+
)
|
|
422
|
+
Phronomy::Tool::ToolExecutor.call_async(
|
|
423
|
+
tool: self,
|
|
424
|
+
args: args,
|
|
425
|
+
cancellation_token: cancellation_token,
|
|
426
|
+
config: config,
|
|
427
|
+
synchronous_call: Operation.synchronous_delegate(self)
|
|
428
|
+
)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def requires_approval
|
|
432
|
+
self.class.requires_approval
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def requires_approval?
|
|
436
|
+
self.class.requires_approval
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
# @api public
|
|
440
|
+
def tool_origin
|
|
441
|
+
:local
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# @api public
|
|
445
|
+
def approval_metadata
|
|
446
|
+
{}
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# @api public
|
|
450
|
+
def execute(**_args)
|
|
451
|
+
raise NotImplementedError, "#{self.class}#execute is not implemented"
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
private
|
|
455
|
+
|
|
456
|
+
# Apply the Tool contract to an implementation that starts asynchronous
|
|
457
|
+
# work. The owner supplies a completion handle; this helper never waits or
|
|
458
|
+
# chooses the implementation, and preserves physical completion via C13.
|
|
459
|
+
def call_async_operation(args, cancellation_token:, operation_name:)
|
|
460
|
+
cancellation_token&.raise_if_cancelled!
|
|
461
|
+
validated_args, schema_error = validate_arguments(args)
|
|
462
|
+
if schema_error
|
|
463
|
+
return Phronomy::AsyncOperation.capture(name: "#{operation_name}-schema") do
|
|
464
|
+
if self.class.on_schema_error == :raise
|
|
465
|
+
raise Phronomy::ToolError, "#{self.class.name} schema error: #{schema_error}"
|
|
466
|
+
end
|
|
467
|
+
"Schema validation failed: #{schema_error}"
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
Phronomy::AsyncOperation.call(name: operation_name,
|
|
471
|
+
on_error: method(:async_error_value),
|
|
472
|
+
transform: method(:truncate_result_if_needed)) do
|
|
473
|
+
source = yield(validated_args || {})
|
|
474
|
+
unless source.respond_to?(:on_complete)
|
|
475
|
+
raise Phronomy::ToolError,
|
|
476
|
+
"#{self.class.name} asynchronous execution must return a completion handle"
|
|
477
|
+
end
|
|
478
|
+
source
|
|
479
|
+
end
|
|
480
|
+
rescue => error
|
|
481
|
+
Phronomy::AsyncOperation.capture(name: operation_name,
|
|
482
|
+
on_error: method(:async_error_value)) { raise error }
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def async_error_value(error)
|
|
486
|
+
if error.is_a?(Phronomy::ToolError) || error.is_a?(Phronomy::CancellationError)
|
|
487
|
+
raise error
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
if self.class.on_error == :suppress
|
|
491
|
+
message = "[Phronomy] Tool #{self.class.name} suppressed error: " \
|
|
492
|
+
"#{error.class}: #{error.message}"
|
|
493
|
+
if Phronomy::RuntimeSettings.current.logger
|
|
494
|
+
Phronomy::RuntimeSettings.current.logger.warn(message)
|
|
495
|
+
else
|
|
496
|
+
warn message
|
|
497
|
+
end
|
|
498
|
+
"Tool error suppressed: #{error.message}"
|
|
499
|
+
else
|
|
500
|
+
wrapped = Phronomy::ToolError.new(
|
|
501
|
+
"#{self.class.name} execution failed: #{error.message}"
|
|
502
|
+
)
|
|
503
|
+
wrapped.set_backtrace(error.backtrace)
|
|
504
|
+
raise wrapped
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def execute_accepts_cancellation_token?
|
|
509
|
+
method(:execute).parameters.any? do |type, name|
|
|
510
|
+
name == :cancellation_token && %i[key keyreq].include?(type)
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def truncate_result_if_needed(result)
|
|
515
|
+
max = self.class.max_result_size || Settings.current.max_result_size
|
|
516
|
+
return result unless max && result.respond_to?(:length) && result.length > max
|
|
517
|
+
|
|
518
|
+
msg = "[Phronomy] Tool #{self.class.name} result truncated " \
|
|
519
|
+
"(#{result.length} chars > #{max} limit)"
|
|
520
|
+
if Phronomy::RuntimeSettings.current.logger
|
|
521
|
+
Phronomy::RuntimeSettings.current.logger.warn(msg)
|
|
522
|
+
else
|
|
523
|
+
warn msg
|
|
524
|
+
end
|
|
525
|
+
"#{result[0, max]}...[truncated]"
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def redacted_args(args)
|
|
529
|
+
redacted = self.class.redact_params
|
|
530
|
+
return args if redacted.empty?
|
|
531
|
+
|
|
532
|
+
args.each_with_object({}) do |(key, value), result|
|
|
533
|
+
result[key] = redacted.include?(key.to_sym) ? "[REDACTED]" : value
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def nested_schema_to_json_schema(nested)
|
|
538
|
+
nested.each_with_object({}) do |(prop_name, spec), result|
|
|
539
|
+
type = spec[:type].to_s
|
|
540
|
+
type = "number" if %w[float double].include?(type)
|
|
541
|
+
type = "integer" if type == "int"
|
|
542
|
+
entry = {"type" => spec[:required] ? type : [type, "null"]}
|
|
543
|
+
entry["description"] = spec[:desc] if spec[:desc]
|
|
544
|
+
entry["enum"] = spec[:required] ? spec[:enum] : (spec[:enum] + [nil]).uniq if spec[:enum]
|
|
545
|
+
if spec[:properties]
|
|
546
|
+
entry["properties"] = nested_schema_to_json_schema(spec[:properties])
|
|
547
|
+
entry["required"] = spec[:properties].select { |_, child| child[:required] }.keys.map(&:to_s)
|
|
548
|
+
entry["additionalProperties"] = false
|
|
549
|
+
end
|
|
550
|
+
result[prop_name.to_s] = entry
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
end
|
|
14
554
|
end
|
|
15
555
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "schema"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Tool
|
|
7
|
+
# A request is a value, not an executable capability or an approval grant.
|
|
8
|
+
# @api public
|
|
9
|
+
class CallRequest
|
|
10
|
+
attr_reader :id, :name, :arguments, :metadata
|
|
11
|
+
|
|
12
|
+
# @api public
|
|
13
|
+
def initialize(id:, name:, arguments:, metadata: {})
|
|
14
|
+
unless id.is_a?(String) && !id.empty? && name.is_a?(String) && !name.empty?
|
|
15
|
+
raise ArgumentError, "Tool request requires non-empty String id and name"
|
|
16
|
+
end
|
|
17
|
+
unless arguments.is_a?(Hash) && metadata.is_a?(Hash)
|
|
18
|
+
raise ArgumentError, "Tool arguments and metadata must be Hashes"
|
|
19
|
+
end
|
|
20
|
+
@id = id.dup.freeze
|
|
21
|
+
@name = name.dup.freeze
|
|
22
|
+
@arguments = Phronomy::Values::Immutable.copy(Schema.json_value(arguments))
|
|
23
|
+
@metadata = Phronomy::Values::Immutable.copy(Schema.json_value(metadata))
|
|
24
|
+
freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @api public
|
|
28
|
+
def self.from_h(value)
|
|
29
|
+
source = Schema.json_value(value)
|
|
30
|
+
new(id: source.fetch("id"), name: source.fetch("name"),
|
|
31
|
+
arguments: source.fetch("arguments"), metadata: source.fetch("metadata", {}))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @api public
|
|
35
|
+
def to_h
|
|
36
|
+
{"id" => id, "name" => name, "arguments" => arguments, "metadata" => metadata}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
module
|
|
5
|
-
class
|
|
4
|
+
module Tool
|
|
5
|
+
class DefinitionSet
|
|
6
6
|
attr_reader :runtime_tools, :definitions
|
|
7
7
|
|
|
8
|
-
def self.build(
|
|
9
|
-
runtime_tools =
|
|
10
|
-
definitions =
|
|
11
|
-
prepared = agent.send(:prepare_tool_class, tool_class)
|
|
8
|
+
def self.build(tools:, runtime_tools: tools)
|
|
9
|
+
runtime_tools = Array(runtime_tools).freeze
|
|
10
|
+
definitions = tools.map do |prepared|
|
|
12
11
|
tool = prepared.is_a?(Class) ? prepared.new : prepared
|
|
13
12
|
{
|
|
14
13
|
"name" => tool.name.to_s,
|
|
@@ -68,7 +67,7 @@ module Phronomy
|
|
|
68
67
|
|
|
69
68
|
current_definition, runtime_tool = current_by_name.fetch(name) do
|
|
70
69
|
raise Phronomy::ConfigurationError,
|
|
71
|
-
"ContextPolicy selected Tool not present in current Agent configuration: #{name}"
|
|
70
|
+
"Phronomy::Context::ContextPolicy selected Tool not present in current Agent configuration: #{name}"
|
|
72
71
|
end
|
|
73
72
|
if definition["parameters_schema"] == {} && current_definition["parameters_schema"] != {}
|
|
74
73
|
raise Phronomy::ConfigurationError,
|
|
@@ -78,7 +77,7 @@ module Phronomy
|
|
|
78
77
|
unless Phronomy::CanonicalJSON.dump(current_definition) ==
|
|
79
78
|
Phronomy::CanonicalJSON.dump(definition)
|
|
80
79
|
raise Phronomy::ConfigurationError,
|
|
81
|
-
"Agent Tool definition changed after ContextPolicy selection: #{name}"
|
|
80
|
+
"Agent Tool definition changed after Phronomy::Context::ContextPolicy selection: #{name}"
|
|
82
81
|
end
|
|
83
82
|
|
|
84
83
|
selected_tools << runtime_tool
|