phronomy 0.27.0 → 0.29.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 +28 -7
- 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/application-recipes.md +229 -0
- 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/dx05-agent-scope.md +119 -0
- data/docs/architecture/dx05-workflow-task-state.md +134 -0
- data/docs/architecture/error-api-review.md +245 -0
- 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/failure-reason-codes.md +45 -0
- data/docs/features.md +12 -4
- data/docs/getting-started.md +52 -48
- 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/ordered-event-delivery.md +54 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/docs/workflow-completion.md +92 -0
- data/gemfiles/http_minimum.gemfile +5 -0
- 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/agent_not_found_error.rb +16 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
- data/lib/phronomy/agent/api/agent.rb +13 -0
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +132 -103
- data/lib/phronomy/agent/base.rb +146 -113
- data/lib/phronomy/agent/before_llm_input.rb +70 -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 +55 -32
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
- 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 +19 -120
- 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 +8 -11
- 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 +188 -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/lifetime_api.rb +76 -0
- data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
- data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
- data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
- data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
- 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 +13 -12
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
- 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} +3 -3
- 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/common/error.rb +65 -1
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/base.rb +7 -2
- 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 +195 -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/authentication_error.rb +11 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/rate_limit_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/{engine → execution}/execution_cancellation_error.rb +2 -2
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
- 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 +9 -4
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
- 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/generation/low_confidence_error.rb +2 -2
- data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
- 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 +60 -44
- 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_busy_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
- data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
- 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/storage/condition_failed_error.rb +2 -2
- data/lib/phronomy/storage/unique_constraint_error.rb +2 -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 +551 -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 +36 -7
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
- 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 +37 -9
- 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 +28 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
- data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
- data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
- data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -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 +61 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +63 -16
- data/scripts/api_snapshot.rb +47 -15
- data/scripts/check_readme_ruby.rb +24 -29
- data/scripts/check_readme_runnable.rb +92 -81
- 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/agent_not_found_error.rbs +6 -0
- data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -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/stream_callback_error.rbs +8 -0
- data/sig/phronomy/agent.rbs +29 -135
- 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 +23 -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 +1 -21
- data/sig/phronomy/filter/filter_block_error.rbs +6 -0
- data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/integrations.rbs +14 -0
- 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/team_busy_error.rbs +8 -0
- data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -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} +4 -18
- 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 +15 -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 +5 -1
- data/sig/phronomy.rbs +4 -49
- metadata +365 -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}/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
|
@@ -1,91 +1,72 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "request"
|
|
4
|
+
require_relative "response"
|
|
5
|
+
require_relative "stream_chunk"
|
|
6
|
+
require_relative "invalid_result_error"
|
|
7
|
+
|
|
3
8
|
module Phronomy
|
|
4
9
|
module LLMAdapter
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# External adapters implement {#complete} and {#stream}. The adapter or the
|
|
8
|
-
# underlying provider client owns transport timeout, retry, backoff, and
|
|
9
|
-
# rate-limit behavior. Phronomy owns cooperative cancellation and isolates
|
|
10
|
-
# synchronous provider calls in {OffloadPool}.
|
|
11
|
-
#
|
|
12
|
-
# The Agent pipeline calls the framework-owned {#complete_async} and
|
|
13
|
-
# {#stream_async} wrappers. Adapter implementers do not need to depend on
|
|
14
|
-
# EventLoop, FSMSession, OffloadPool, AgentInvocation, or ExecutionCoordinator.
|
|
15
|
-
#
|
|
16
|
-
# The current input to this SPI is the configured/materialized chat runtime
|
|
17
|
-
# object. Formalizing this SPI therefore does not imply a provider-neutral
|
|
18
|
-
# replacement for RubyLLMMaterializer.
|
|
19
|
-
#
|
|
10
|
+
# Owns one-turn operations. Backends implement protected synchronous hooks.
|
|
11
|
+
# SDK construction and translation happen entirely inside those hooks.
|
|
20
12
|
# @api public
|
|
21
13
|
class Base
|
|
22
|
-
# Performs a blocking (non-streaming) LLM completion.
|
|
23
|
-
#
|
|
24
|
-
# Implementors call the configured chat/runtime client and return its
|
|
25
|
-
# response object. Transport/retry policy remains adapter-owned.
|
|
26
|
-
#
|
|
27
|
-
# @param chat [Object] the configured/materialized chat runtime object
|
|
28
|
-
# @param message [String, nil] user message, or nil to continue without adding a new user turn
|
|
29
|
-
# @param config [Hash] invocation config (e.g. +:cancellation_token+)
|
|
30
|
-
# @return [Object] LLM response object
|
|
31
|
-
# @raise [NotImplementedError]
|
|
32
14
|
# @api public
|
|
33
|
-
def
|
|
34
|
-
|
|
15
|
+
def identity
|
|
16
|
+
{"adapter_name" => self.class.name}.freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Local model metadata only. Missing capacity is permitted.
|
|
20
|
+
# @api public
|
|
21
|
+
def input_budget(model_config)
|
|
22
|
+
nil
|
|
35
23
|
end
|
|
36
24
|
|
|
37
|
-
# Performs a blocking streaming LLM completion.
|
|
38
|
-
#
|
|
39
|
-
# @param chat [Object] the configured/materialized chat runtime object
|
|
40
|
-
# @param message [String, nil] user message, or nil to continue without adding a new user turn
|
|
41
|
-
# @param config [Hash] invocation config
|
|
42
|
-
# @yield [chunk] streaming chunk from the LLM
|
|
43
|
-
# @return [Object] LLM response object
|
|
44
|
-
# @raise [NotImplementedError]
|
|
45
25
|
# @api public
|
|
46
|
-
def
|
|
47
|
-
|
|
26
|
+
def complete(request, cancellation_token: nil)
|
|
27
|
+
validate_request!(request, cancellation_token)
|
|
28
|
+
response = perform_complete(request, cancellation_token: cancellation_token)
|
|
29
|
+
cancellation_token&.raise_if_cancelled!
|
|
30
|
+
validate_response!(response)
|
|
48
31
|
end
|
|
49
32
|
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
complete(chat, message, config: config)
|
|
33
|
+
# Notifications run on the caller thread. AsyncClient supplies an internal
|
|
34
|
+
# event sink; application callbacks remain with their owning domain.
|
|
35
|
+
# @api public
|
|
36
|
+
def stream(request, cancellation_token: nil)
|
|
37
|
+
raise ArgumentError, "stream requires a block" unless block_given?
|
|
38
|
+
validate_request!(request, cancellation_token)
|
|
39
|
+
response = perform_stream(request, cancellation_token: cancellation_token) do |chunk|
|
|
40
|
+
cancellation_token&.raise_if_cancelled!
|
|
41
|
+
raise InvalidResultError, "stream must yield StreamChunk values" unless chunk.is_a?(StreamChunk)
|
|
42
|
+
yield chunk
|
|
61
43
|
end
|
|
44
|
+
cancellation_token&.raise_if_cancelled!
|
|
45
|
+
validate_response!(response)
|
|
62
46
|
end
|
|
63
47
|
|
|
64
|
-
|
|
65
|
-
#
|
|
66
|
-
# The block is invoked on an OffloadPool worker thread. Agent code must pass
|
|
67
|
-
# only a lightweight internal sink that posts a value to EventLoop;
|
|
68
|
-
# application callbacks must never be passed directly to this method.
|
|
69
|
-
#
|
|
70
|
-
# @yield [chunk] streaming chunk on the worker thread
|
|
71
|
-
# @return [Phronomy::TaskResult] caller-facing completion handle
|
|
72
|
-
# @api private
|
|
73
|
-
def stream_async(chat, message, config: {}, pool: default_pool, &block)
|
|
74
|
-
raise ArgumentError, "stream_async requires a block" unless block
|
|
48
|
+
protected
|
|
75
49
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
50
|
+
# @api public
|
|
51
|
+
def perform_complete(request, cancellation_token:)
|
|
52
|
+
raise NotImplementedError, "#{self.class}#perform_complete is not implemented"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @api public
|
|
56
|
+
def perform_stream(request, cancellation_token:, &block)
|
|
57
|
+
raise NotImplementedError, "#{self.class}#perform_stream is not implemented"
|
|
83
58
|
end
|
|
84
59
|
|
|
85
60
|
private
|
|
86
61
|
|
|
87
|
-
def
|
|
88
|
-
|
|
62
|
+
def validate_request!(request, token)
|
|
63
|
+
token&.raise_if_cancelled!
|
|
64
|
+
raise ArgumentError, "LLM operation requires a Request" unless request.is_a?(Request)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def validate_response!(response)
|
|
68
|
+
raise InvalidResultError, "LLM operation must return a Response" unless response.is_a?(Response)
|
|
69
|
+
response
|
|
89
70
|
end
|
|
90
71
|
end
|
|
91
72
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../common/values/immutable"
|
|
4
|
+
require_relative "../tool/schema"
|
|
5
|
+
require_relative "../tool/call_request"
|
|
6
|
+
|
|
7
|
+
module Phronomy
|
|
8
|
+
module LLMAdapter
|
|
9
|
+
# Provider-independent conversation value, with no executable capabilities.
|
|
10
|
+
# @api public
|
|
11
|
+
class Message
|
|
12
|
+
attr_reader :role, :content, :tool_calls, :tool_call_id, :metadata
|
|
13
|
+
|
|
14
|
+
# @api public
|
|
15
|
+
def initialize(role:, content: nil, tool_calls: [], tool_call_id: nil, metadata: {})
|
|
16
|
+
unless role.respond_to?(:to_sym) && %i[system user assistant tool].include?(role.to_sym)
|
|
17
|
+
raise ArgumentError, "invalid LLM message role"
|
|
18
|
+
end
|
|
19
|
+
unless content.nil? || content.is_a?(String) || content.is_a?(Array) || content.is_a?(Hash)
|
|
20
|
+
raise ArgumentError, "LLM content must be text, structured JSON or nil"
|
|
21
|
+
end
|
|
22
|
+
unless tool_calls.is_a?(Array) && tool_calls.all? { |call| call.is_a?(Phronomy::Tool::CallRequest) }
|
|
23
|
+
raise ArgumentError, "LLM Tool calls must be Tool::CallRequest values"
|
|
24
|
+
end
|
|
25
|
+
ids = tool_calls.map(&:id)
|
|
26
|
+
raise ArgumentError, "duplicate LLM Tool call id" unless ids.uniq == ids
|
|
27
|
+
raise ArgumentError, "only assistant messages can request Tools" if !tool_calls.empty? && role.to_sym != :assistant
|
|
28
|
+
if role.to_sym == :tool && (!tool_call_id.is_a?(String) || tool_call_id.empty?)
|
|
29
|
+
raise ArgumentError, "Tool message requires tool_call_id"
|
|
30
|
+
end
|
|
31
|
+
unless tool_call_id.nil? || tool_call_id.is_a?(String)
|
|
32
|
+
raise ArgumentError, "tool_call_id must be a String"
|
|
33
|
+
end
|
|
34
|
+
raise ArgumentError, "LLM metadata must be a Hash" unless metadata.is_a?(Hash)
|
|
35
|
+
Phronomy::Values::Immutable.validate_canonical_json!(content, label: "LLM content")
|
|
36
|
+
Phronomy::Values::Immutable.validate_canonical_json!(metadata, label: "LLM metadata")
|
|
37
|
+
@role = role.to_sym
|
|
38
|
+
@content = Phronomy::Values::Immutable.copy(content)
|
|
39
|
+
@tool_calls = tool_calls.dup.freeze
|
|
40
|
+
@tool_call_id = tool_call_id&.dup&.freeze
|
|
41
|
+
@metadata = Phronomy::Values::Immutable.copy(metadata)
|
|
42
|
+
freeze
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @api public
|
|
46
|
+
def tool_call?
|
|
47
|
+
!tool_calls.empty?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @api public
|
|
51
|
+
def content_present?
|
|
52
|
+
!content.nil? && !content.empty?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @api public
|
|
56
|
+
def to_h
|
|
57
|
+
{"role" => role.to_s, "content" => content, "tool_calls" => tool_calls.map(&:to_h),
|
|
58
|
+
"tool_call_id" => tool_call_id, "metadata" => metadata}.compact
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "message"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module LLMAdapter
|
|
7
|
+
# Immutable input for one model turn, including inert Tool definitions.
|
|
8
|
+
# @api public
|
|
9
|
+
class Request
|
|
10
|
+
attr_reader :model_config, :system, :messages, :tools, :message
|
|
11
|
+
|
|
12
|
+
# @api public
|
|
13
|
+
def initialize(model_config: {}, system: nil, messages: [], tools: [], message: nil)
|
|
14
|
+
if !(model_config.is_a?(Hash) && (system.nil? || system.is_a?(String)) && (message.nil? || message.is_a?(String)))
|
|
15
|
+
raise ArgumentError, "invalid LLM request configuration or text"
|
|
16
|
+
end
|
|
17
|
+
unless messages.is_a?(Array) && messages.all? { |item| item.is_a?(Message) }
|
|
18
|
+
raise ArgumentError, "LLM request messages must be Message values"
|
|
19
|
+
end
|
|
20
|
+
unless tools.is_a?(Array) && tools.all? { |tool| tool.is_a?(Hash) }
|
|
21
|
+
raise ArgumentError, "LLM request tools must be definition Hashes"
|
|
22
|
+
end
|
|
23
|
+
normalized_tools = tools.map do |definition|
|
|
24
|
+
definition = normalize_keys(definition)
|
|
25
|
+
name = definition["name"]
|
|
26
|
+
unless name.is_a?(String) && !name.empty? && definition["description"].is_a?(String)
|
|
27
|
+
raise ArgumentError, "Tool definition requires name and description"
|
|
28
|
+
end
|
|
29
|
+
if definition.key?("provider_options") && !definition["provider_options"].is_a?(Hash)
|
|
30
|
+
raise ArgumentError, "Tool provider_options must be a Hash"
|
|
31
|
+
end
|
|
32
|
+
Phronomy::Tool::Schema.new(definition.fetch("parameters_schema"))
|
|
33
|
+
Phronomy::Values::Immutable.validate_canonical_json!(definition, label: "Tool definition")
|
|
34
|
+
definition
|
|
35
|
+
end
|
|
36
|
+
names = normalized_tools.map { |tool| tool.fetch("name") }
|
|
37
|
+
raise ArgumentError, "duplicate Tool definition name" unless names.uniq == names
|
|
38
|
+
config = normalize_keys(model_config)
|
|
39
|
+
Phronomy::Values::Immutable.validate_canonical_json!(config, label: "LLM model configuration")
|
|
40
|
+
max = config["max_output_tokens"]
|
|
41
|
+
if !max.nil? && (!max.is_a?(Integer) || max <= 0)
|
|
42
|
+
raise ArgumentError, "max_output_tokens must be a positive Integer"
|
|
43
|
+
end
|
|
44
|
+
%w[model provider].each do |key|
|
|
45
|
+
value = config[key]
|
|
46
|
+
if !(value.nil? || (value.is_a?(String) && !value.empty?))
|
|
47
|
+
raise ArgumentError, "#{key} must be a non-empty String or nil"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
%w[assume_model_exists cache_instructions].each do |key|
|
|
51
|
+
raise ArgumentError, "#{key} must be boolean or nil" unless [true, false, nil].include?(config[key])
|
|
52
|
+
end
|
|
53
|
+
temperature = config["temperature"]
|
|
54
|
+
if !(temperature.nil? || (temperature.is_a?(Numeric) && temperature.finite?))
|
|
55
|
+
raise ArgumentError, "temperature must be a finite number or nil"
|
|
56
|
+
end
|
|
57
|
+
@model_config = Phronomy::Values::Immutable.copy(config)
|
|
58
|
+
@system = system&.dup&.freeze
|
|
59
|
+
@message = message&.dup&.freeze
|
|
60
|
+
@messages = messages.dup.freeze
|
|
61
|
+
@tools = Phronomy::Values::Immutable.copy(normalized_tools)
|
|
62
|
+
freeze
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def normalize_keys(value)
|
|
68
|
+
value.each_with_object({}) do |(key, child), result|
|
|
69
|
+
unless key.is_a?(String) || key.is_a?(Symbol)
|
|
70
|
+
raise ArgumentError, "LLM configuration/definition keys must be Strings or Symbols"
|
|
71
|
+
end
|
|
72
|
+
name = key.to_s
|
|
73
|
+
raise ArgumentError, "duplicate LLM configuration/definition key: #{name}" if result.key?(name)
|
|
74
|
+
result[name] = child
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "message"
|
|
4
|
+
require_relative "token_usage"
|
|
5
|
+
|
|
6
|
+
module Phronomy
|
|
7
|
+
module LLMAdapter
|
|
8
|
+
# Completed assistant turn. Tool requests do not authorize execution.
|
|
9
|
+
# @api public
|
|
10
|
+
class Response < Message
|
|
11
|
+
attr_reader :usage
|
|
12
|
+
|
|
13
|
+
# @api public
|
|
14
|
+
def initialize(content: nil, tool_calls: [], usage: Phronomy::LLMAdapter::TokenUsage.new, metadata: {}, role: :assistant)
|
|
15
|
+
raise ArgumentError, "LLM response must be an assistant message" unless role == :assistant || role == "assistant"
|
|
16
|
+
raise ArgumentError, "LLM usage must be LLMAdapter::TokenUsage" unless usage.is_a?(Phronomy::LLMAdapter::TokenUsage)
|
|
17
|
+
@usage = usage
|
|
18
|
+
super(role: :assistant, content: content, tool_calls: tool_calls, metadata: metadata)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @api public
|
|
22
|
+
def self.from_h(value)
|
|
23
|
+
source = value.transform_keys(&:to_s)
|
|
24
|
+
new(role: source.fetch("role", "assistant"), content: source["content"],
|
|
25
|
+
tool_calls: source.fetch("tool_calls", []).map { |call| Phronomy::Tool::CallRequest.from_h(call) },
|
|
26
|
+
usage: Phronomy::LLMAdapter::TokenUsage.new(**source.fetch("usage", {}).transform_keys(&:to_sym)),
|
|
27
|
+
metadata: source.fetch("metadata", {}))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @api public
|
|
31
|
+
def to_h
|
|
32
|
+
super.merge("usage" => usage.to_h.transform_keys(&:to_s))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module LLMAdapter
|
|
5
|
+
# Intermediate text notification; the final Response is authoritative.
|
|
6
|
+
# @api public
|
|
7
|
+
class StreamChunk
|
|
8
|
+
attr_reader :content
|
|
9
|
+
|
|
10
|
+
# @api public
|
|
11
|
+
def initialize(content:)
|
|
12
|
+
raise ArgumentError, "stream content must be a String or nil" unless content.nil? || content.is_a?(String)
|
|
13
|
+
@content = content&.dup&.freeze
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module LLMAdapter
|
|
5
|
+
# Immutable usage counts. An omitted count remains nil; zero is a reported
|
|
6
|
+
# measurement. SDK field translation belongs to the backend.
|
|
7
|
+
# @api public
|
|
8
|
+
class TokenUsage
|
|
9
|
+
attr_reader :input, :output, :cached, :cache_creation
|
|
10
|
+
|
|
11
|
+
def initialize(input: nil, output: nil, cached: nil, cache_creation: nil)
|
|
12
|
+
[input, output, cached, cache_creation].each do |value|
|
|
13
|
+
if !(value.nil? || (value.is_a?(Integer) && value >= 0))
|
|
14
|
+
raise ArgumentError, "token usage must be non-negative Integers or nil"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
@input = input
|
|
18
|
+
@output = output
|
|
19
|
+
@cached = cached
|
|
20
|
+
@cache_creation = cache_creation
|
|
21
|
+
freeze
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns a zero-valued TokenUsage suitable as an accumulator seed.
|
|
25
|
+
def self.zero
|
|
26
|
+
new(input: 0, output: 0, cached: 0, cache_creation: 0)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Adds two TokenUsage instances. nil fields are treated as 0 so that
|
|
30
|
+
# partially-reported usage accumulates correctly.
|
|
31
|
+
# When other is nil, returns self unchanged.
|
|
32
|
+
def +(other)
|
|
33
|
+
return self if other.nil?
|
|
34
|
+
|
|
35
|
+
self.class.new(
|
|
36
|
+
input: _add(input, other.input),
|
|
37
|
+
output: _add(output, other.output),
|
|
38
|
+
cached: _add(cached, other.cached),
|
|
39
|
+
cache_creation: _add(cache_creation, other.cache_creation)
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def ==(other)
|
|
44
|
+
other.is_a?(TokenUsage) &&
|
|
45
|
+
input == other.input &&
|
|
46
|
+
output == other.output &&
|
|
47
|
+
cached == other.cached &&
|
|
48
|
+
cache_creation == other.cache_creation
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_h
|
|
52
|
+
{input: input, output: output, cached: cached, cache_creation: cache_creation}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def _add(a, b)
|
|
58
|
+
return nil if a.nil? && b.nil?
|
|
59
|
+
|
|
60
|
+
(a || 0) + (b || 0)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/phronomy/llm_adapter.rb
CHANGED
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# {LLMAdapter::Base#stream}. Those methods receive Phronomy's currently
|
|
8
|
-
# materialized chat/runtime object and perform the provider call. Phronomy owns
|
|
9
|
-
# the async/offload bridge around that synchronous contract.
|
|
10
|
-
#
|
|
11
|
-
# This SPI does not by itself make the complete input-materialization pipeline
|
|
12
|
-
# provider-neutral: the current Agent pipeline still materializes the canonical
|
|
13
|
-
# LLM input through RubyLLM-specific runtime objects before invoking the adapter.
|
|
14
|
-
#
|
|
15
|
-
# @example Configuring a custom adapter
|
|
16
|
-
# Phronomy.configure do |c|
|
|
17
|
-
# c.llm_adapter = MyCustomAdapter.new
|
|
18
|
-
# end
|
|
19
|
-
#
|
|
4
|
+
# LLM domain operations and public request, response, usage and failure values.
|
|
5
|
+
# A backend implements Base's protected hooks. SDK objects remain inside the
|
|
6
|
+
# backend; AsyncClient executes the entire synchronous operation on a worker.
|
|
20
7
|
# @api public
|
|
21
8
|
module LLMAdapter
|
|
22
9
|
end
|
|
@@ -2,18 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module MultiAgent
|
|
5
|
-
#
|
|
5
|
+
# Environment-local admission for one synchronous coordination call per owner
|
|
6
6
|
# (a Handoff main Agent or TeamCoordinator). This does not own durable runs
|
|
7
7
|
# or active-agent mutation; those remain in their existing domains.
|
|
8
8
|
# @api private
|
|
9
9
|
class AdmissionRegistry
|
|
10
|
-
# Construction has no side effects. Runtime returns the registered instance
|
|
11
|
-
# when callers concurrently supply candidates for the same key.
|
|
12
|
-
# @api private
|
|
13
|
-
def self.for(runtime)
|
|
14
|
-
runtime.__register_shutdown_participant(key: self, participant: new)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
10
|
def initialize
|
|
18
11
|
@mutex = Mutex.new
|
|
19
12
|
@cond = ConditionVariable.new
|