phronomy 0.26.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 +92 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +13 -3
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -47
- data/benchmark/bench_context_assembler.rb +17 -15
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +13 -0
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +9 -3
- data/docs/architecture/context-management.md +31 -1
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -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 +86 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +11 -4
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +45 -4
- 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 +343 -0
- data/docs/architecture/remaining-refactoring-plan.md +390 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +56 -0
- data/docs/architecture/tracing.md +2 -2
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +222 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
- data/docs/decisions/032-storage-backend-composition.md +85 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
- data/docs/decisions/040-configuration-default-composition.md +105 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +83 -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 +52 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +16 -5
- data/docs/getting-started.md +11 -8
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
- data/docs/migrations/handoff-runner-multi-agent.md +37 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -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/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +190 -215
- data/docs/runtime-and-concurrency.md +142 -32
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/agent/async_event_api.rb +38 -33
- data/lib/phronomy/agent/base.rb +106 -294
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_importer.rb +2 -2
- data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
- data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- 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_context.rb → handoff/transfer_context.rb} +6 -6
- data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
- data/lib/phronomy/agent/handoff_error.rb +7 -0
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
- data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +338 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
- 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 +29 -0
- data/lib/phronomy/agent/persistence/watermark.rb +29 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
- 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/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
- data/lib/phronomy/agent/saved_context_reader.rb +55 -0
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/state_writer.rb +166 -0
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
- 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/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +12 -0
- data/lib/phronomy/content_store/stored_contents.rb +44 -0
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
- data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
- data/lib/phronomy/{agent → 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}/llm_input_build_context.rb +2 -2
- data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
- data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
- 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 +39 -20
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
- data/lib/phronomy/engine/event_loop.rb +164 -630
- 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 +9 -0
- data/lib/phronomy/engine/fsm_session.rb +37 -30
- data/lib/phronomy/engine/metrics.rb +28 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +104 -95
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/execution/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
- data/lib/phronomy/execution/cancellation_error.rb +7 -0
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -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/execution/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
- data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
- data/lib/phronomy/execution/timeout_error.rb +7 -0
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
- 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/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
- 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 → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +75 -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 +124 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
- 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/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- 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 +21 -21
- 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 +29 -0
- 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 +11 -0
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -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/global_runtime.rb +19 -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 +107 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- 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 +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- 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_definition_set.rb → tool/definition_set.rb} +16 -12
- 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/{agent → tool}/tool_executor.rb +10 -7
- 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.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
- 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/runtime_binding/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy.rb +97 -98
- data/scripts/api_snapshot.rb +36 -16
- data/scripts/storage_spi_snapshot.rb +36 -0
- 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 -139
- 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 +12 -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 +24 -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 +3 -185
- 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/contracts.rbs +160 -0
- data/sig/phronomy/tool.rbs +58 -5
- 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 +462 -130
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
- 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 -507
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- 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_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/handoff_request.rb +0 -45
- data/lib/phronomy/agent/handoff_runner.rb +0 -178
- data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
- data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration.rb +0 -59
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/metrics.rb +0 -26
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/recovery.rb +0 -186
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/runnable.rb +0 -54
- data/lib/phronomy/token_usage.rb +0 -85
- 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_recovery.rb +0 -123
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
data/lib/phronomy/persistence.rb
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
class Persistence
|
|
5
|
-
class ConflictError < Phronomy::Error; end
|
|
6
|
-
class NotFoundError < Phronomy::Error; end
|
|
7
|
-
class UnsupportedBackendError < Phronomy::Error; end
|
|
8
|
-
class SerializationError < Phronomy::Error; end
|
|
9
|
-
|
|
10
|
-
REQUIRED_CAPABILITIES = {
|
|
11
|
-
atomic_all: true,
|
|
12
|
-
atomic_admission: true,
|
|
13
|
-
optimistic_revision: true
|
|
14
|
-
}.freeze
|
|
15
|
-
|
|
16
|
-
# Content-addressed immutable content repository. ContentStore has its own
|
|
17
|
-
# codec/canonicalization boundary and is not wrapped in DurableRecord.
|
|
18
|
-
#
|
|
19
|
-
# @return [Object]
|
|
20
|
-
# @api public
|
|
21
|
-
attr_reader :contents
|
|
22
|
-
|
|
23
|
-
# Runtime/domain-facing AgentRoot repository. The backend repository supplied
|
|
24
|
-
# to #initialize is record-oriented; this facade owns encode/decode.
|
|
25
|
-
#
|
|
26
|
-
# @return [Object]
|
|
27
|
-
# @api public
|
|
28
|
-
attr_reader :agents
|
|
29
|
-
|
|
30
|
-
# Runtime/domain-facing append-only Agent Journal repository.
|
|
31
|
-
#
|
|
32
|
-
# @return [Object]
|
|
33
|
-
# @api public
|
|
34
|
-
attr_reader :journals
|
|
35
|
-
|
|
36
|
-
# Runtime/domain-facing AgentExecution repository.
|
|
37
|
-
#
|
|
38
|
-
# @return [Object]
|
|
39
|
-
# @api public
|
|
40
|
-
attr_reader :executions
|
|
41
|
-
|
|
42
|
-
# Runtime/domain-facing durable Workflow snapshot repository.
|
|
43
|
-
#
|
|
44
|
-
# @return [Object]
|
|
45
|
-
# @api public
|
|
46
|
-
attr_reader :workflow_states
|
|
47
|
-
|
|
48
|
-
# Purpose-specific durable Handoff and Team repositories in atomic_all.
|
|
49
|
-
# @api public
|
|
50
|
-
attr_reader :handoff_states, :teams, :team_executions
|
|
51
|
-
|
|
52
|
-
# Read-only exact Agent execution result; never creates a live Agent owner.
|
|
53
|
-
# Result is nil until a result_ref exists; errors remain canonical data.
|
|
54
|
-
# @api public
|
|
55
|
-
def execution_result(execution_id)
|
|
56
|
-
assert_observation_thread!
|
|
57
|
-
execution = executions.load(execution_id)
|
|
58
|
-
{
|
|
59
|
-
execution_id: execution.execution_id, agent_id: execution.agent_id,
|
|
60
|
-
status: execution.status, phase: execution.phase,
|
|
61
|
-
result_ref: execution.result_ref, error_ref: execution.error_ref,
|
|
62
|
-
result: execution.result_ref && contents.fetch_text(execution.result_ref),
|
|
63
|
-
error: execution.error_ref && contents.fetch_json(execution.error_ref)
|
|
64
|
-
}.then { |value| Phronomy::Agent::Immutable.copy(value) }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Follows one exact Handoff turn without loading any Agent or graph.
|
|
68
|
-
# A committed absent Target reservation remains active; failed reads raise.
|
|
69
|
-
# @api public
|
|
70
|
-
def handoff_result(execution_id, main_agent_id: nil)
|
|
71
|
-
assert_observation_thread!
|
|
72
|
-
anchor = main_agent_id&.to_s
|
|
73
|
-
seen = {}
|
|
74
|
-
current = execution_id.to_s
|
|
75
|
-
reserved_agent_id = nil
|
|
76
|
-
loop do
|
|
77
|
-
raise Phronomy::Persistence::SerializationError, "Cyclic durable Handoff chain" if seen[current]
|
|
78
|
-
seen[current] = true
|
|
79
|
-
begin
|
|
80
|
-
execution = executions.load(current)
|
|
81
|
-
rescue Phronomy::Persistence::NotFoundError
|
|
82
|
-
routing = handoff_states.load(anchor)
|
|
83
|
-
if routing && Array(routing.metadata["cancelled_execution_ids"]).include?(current)
|
|
84
|
-
return {execution_id: current, agent_id: reserved_agent_id, status: :cancelled, result: nil, error: nil}.freeze
|
|
85
|
-
end
|
|
86
|
-
if reserved_agent_id && routing && routing.phase != "stable" && routing.pending_target_execution_id == current
|
|
87
|
-
return {execution_id: current, agent_id: reserved_agent_id, status: :active,
|
|
88
|
-
phase: :target_pending, reserved: true, result: nil, error: nil}.freeze
|
|
89
|
-
end
|
|
90
|
-
raise
|
|
91
|
-
end
|
|
92
|
-
anchor ||= execution.metadata.dig("coordination", "main_agent_id")
|
|
93
|
-
unless anchor && execution.metadata.dig("coordination", "main_agent_id") == anchor
|
|
94
|
-
raise Phronomy::Persistence::ConflictError, "Execution does not belong to this Handoff anchor"
|
|
95
|
-
end
|
|
96
|
-
return execution_result(current) unless execution.status == :handed_off
|
|
97
|
-
reserved_agent_id = execution.metadata.fetch("handoff_target_agent_id")
|
|
98
|
-
current = execution.metadata.fetch("handoff_target_execution_id")
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Lists retained active and terminal executions in lexical ID order.
|
|
103
|
-
# after is an exclusive ID cursor; discovery does not imply request dedup.
|
|
104
|
-
# @api public
|
|
105
|
-
def list_executions(agent_id, after: nil, limit: 100)
|
|
106
|
-
assert_observation_thread!
|
|
107
|
-
executions.list(agent_id, after: after, limit: limit)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Read-only exact Team result, independent of current Team class/Proc wiring.
|
|
111
|
-
# @api public
|
|
112
|
-
def team_execution_result(team_execution_id)
|
|
113
|
-
assert_observation_thread!
|
|
114
|
-
execution = team_executions.load(team_execution_id)
|
|
115
|
-
{
|
|
116
|
-
team_execution_id: execution.team_execution_id, team_id: execution.team_id,
|
|
117
|
-
status: execution.status, phase: execution.phase,
|
|
118
|
-
result_ref: execution.result_ref, error_ref: execution.error_ref,
|
|
119
|
-
result: execution.result_ref && contents.fetch_json(execution.result_ref),
|
|
120
|
-
error: execution.error_ref && contents.fetch_json(execution.error_ref)
|
|
121
|
-
}.then { |value| Phronomy::Agent::Immutable.copy(value) }
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Lists retained Team runs; no continuation or callback delivery occurs.
|
|
125
|
-
# @api public
|
|
126
|
-
def list_team_executions(team_id, after: nil, limit: 100)
|
|
127
|
-
assert_observation_thread!
|
|
128
|
-
team_executions.list(team_id, after: after, limit: limit)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Initializes a Persistence backend from record-oriented storage
|
|
132
|
-
# repositories.
|
|
133
|
-
#
|
|
134
|
-
# Except for +contents+, backend repositories exchange
|
|
135
|
-
# {Phronomy::Persistence::DurableRecord} values. Phronomy's repository
|
|
136
|
-
# facades own current-format validation and domain-object encode/decode.
|
|
137
|
-
#
|
|
138
|
-
# Identity, revision, admission, and index metadata needed by a Backend is
|
|
139
|
-
# passed explicitly as repository arguments. A Backend must not inspect
|
|
140
|
-
# DurableRecord#payload to rediscover Phronomy domain semantics.
|
|
141
|
-
#
|
|
142
|
-
# Required raw repository shapes:
|
|
143
|
-
# - agents:
|
|
144
|
-
# create(agent_id:, agent_revision:, record:), load(id),
|
|
145
|
-
# save(id, expected_revision:, next_revision:, record:), delete(id)
|
|
146
|
-
# - journals:
|
|
147
|
-
# append(id, expected_position:, records:, record_ids:), read/head/delete
|
|
148
|
-
# - executions:
|
|
149
|
-
# create_active(execution_id:, agent_id:, execution_revision:, record:),
|
|
150
|
-
# load(id), save(id, expected_revision:, next_revision:, agent_id:,
|
|
151
|
-
# active:, record:), list_active/delete/delete_for_agent/assert_idle!
|
|
152
|
-
# - workflow_states:
|
|
153
|
-
# load(id), save(id, expected_revision:, next_revision:, record:), delete
|
|
154
|
-
#
|
|
155
|
-
# Subclasses normally construct backend-specific raw repository objects and
|
|
156
|
-
# call +super+. Construction fails fast when required capabilities are not
|
|
157
|
-
# advertised.
|
|
158
|
-
#
|
|
159
|
-
# @api public
|
|
160
|
-
def initialize(contents:, agents:, journals:, executions:, workflow_states:, handoff_states:, teams:, team_executions:)
|
|
161
|
-
@contents = contents
|
|
162
|
-
@agents = RepositoryFacades::Agents.new(agents)
|
|
163
|
-
@journals = RepositoryFacades::Journals.new(journals)
|
|
164
|
-
@executions = RepositoryFacades::Executions.new(executions)
|
|
165
|
-
@workflow_states = RepositoryFacades::WorkflowStates.new(workflow_states)
|
|
166
|
-
@handoff_states = RepositoryFacades::HandoffStates.new(handoff_states)
|
|
167
|
-
@teams = RepositoryFacades::Teams.new(teams)
|
|
168
|
-
@team_executions = RepositoryFacades::TeamExecutions.new(team_executions)
|
|
169
|
-
validate_capabilities!
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# Declares storage semantics provided by this backend.
|
|
173
|
-
#
|
|
174
|
-
# Required meanings:
|
|
175
|
-
# - +atomic_all+: all durable repositories can participate in one atomic
|
|
176
|
-
# transaction domain.
|
|
177
|
-
# - +atomic_admission+: Agent execution admission is atomic; at most one
|
|
178
|
-
# active/suspended execution may be admitted for one Agent. This does not
|
|
179
|
-
# mean cross-process Workflow admission or distributed locking.
|
|
180
|
-
# - +optimistic_revision+: Agent, Execution, Workflow revision checks and
|
|
181
|
-
# Journal position checks provide compare-and-swap conflict detection.
|
|
182
|
-
#
|
|
183
|
-
# @return [Hash{Symbol => Boolean}]
|
|
184
|
-
# @api public
|
|
185
|
-
def capabilities
|
|
186
|
-
{
|
|
187
|
-
atomic_all: false,
|
|
188
|
-
atomic_admission: false,
|
|
189
|
-
optimistic_revision: false
|
|
190
|
-
}.freeze
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
# Executes one atomic durable transaction.
|
|
194
|
-
#
|
|
195
|
-
# The yielded object is a transaction-scoped Persistence view exposing the
|
|
196
|
-
# same domain-facing repository facades. Its underlying backend repositories
|
|
197
|
-
# remain DurableRecord-oriented.
|
|
198
|
-
#
|
|
199
|
-
# If the block raises, mutations made through the transaction view must not
|
|
200
|
-
# be committed. Storage failures whose commit outcome is fundamentally
|
|
201
|
-
# unknown remain backend/database failures; Phronomy does not claim
|
|
202
|
-
# exactly-once semantics for such failures.
|
|
203
|
-
#
|
|
204
|
-
# @yieldparam transaction_view [Object]
|
|
205
|
-
# @return [Object] the block result
|
|
206
|
-
# @raise [UnsupportedBackendError] when atomic transactions are unavailable
|
|
207
|
-
# @api public
|
|
208
|
-
def transaction
|
|
209
|
-
raise UnsupportedBackendError, "#{self.class} does not provide atomic_all"
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# Backend SPI helper for transaction implementations whose transaction-scoped
|
|
213
|
-
# raw repositories differ from the root repository objects.
|
|
214
|
-
#
|
|
215
|
-
# +watermark+ is a transaction-scoped object responding to
|
|
216
|
-
# +assert_agent_watermark!+. The returned view owns the same Phronomy codec
|
|
217
|
-
# facades as the root Persistence instance, so backend authors never need to
|
|
218
|
-
# instantiate RepositoryFacades directly.
|
|
219
|
-
#
|
|
220
|
-
# Backends whose repository objects are already transaction-scoped may yield
|
|
221
|
-
# +self+ and need not call this helper.
|
|
222
|
-
#
|
|
223
|
-
# @api public
|
|
224
|
-
def build_transaction_view(
|
|
225
|
-
contents:,
|
|
226
|
-
agents:,
|
|
227
|
-
journals:,
|
|
228
|
-
executions:,
|
|
229
|
-
workflow_states:,
|
|
230
|
-
handoff_states:, teams:, team_executions:,
|
|
231
|
-
watermark:
|
|
232
|
-
)
|
|
233
|
-
RepositoryFacades::View.new(
|
|
234
|
-
contents: contents,
|
|
235
|
-
agents: agents,
|
|
236
|
-
journals: journals,
|
|
237
|
-
executions: executions,
|
|
238
|
-
workflow_states: workflow_states,
|
|
239
|
-
handoff_states: handoff_states, teams: teams, team_executions: team_executions,
|
|
240
|
-
watermark: watermark
|
|
241
|
-
)
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Verifies that a live Agent still owns the durable base it hydrated.
|
|
245
|
-
#
|
|
246
|
-
# This is a Backend SPI operation invoked by Phronomy at durable barriers.
|
|
247
|
-
# Ordinary application code should not call it directly. The backend must
|
|
248
|
-
# compare the stored Agent revision and current Journal position against the
|
|
249
|
-
# supplied watermark in the same storage consistency view used by subsequent
|
|
250
|
-
# writes in the surrounding transaction.
|
|
251
|
-
#
|
|
252
|
-
# @api public
|
|
253
|
-
def assert_agent_watermark!(agent_id:, agent_revision:, journal_position:)
|
|
254
|
-
raise UnsupportedBackendError,
|
|
255
|
-
"#{self.class} does not provide Agent durable-watermark checks"
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
private
|
|
259
|
-
|
|
260
|
-
def assert_observation_thread!
|
|
261
|
-
if Phronomy::Runtime.in_event_loop_context?
|
|
262
|
-
raise Phronomy::EventLoopReentrancyError, "Persistence observation cannot block EventLoop"
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def validate_capabilities!
|
|
267
|
-
missing = REQUIRED_CAPABILITIES.reject do |key, value|
|
|
268
|
-
capabilities[key] == value
|
|
269
|
-
end
|
|
270
|
-
return if missing.empty?
|
|
271
|
-
|
|
272
|
-
raise UnsupportedBackendError,
|
|
273
|
-
"Persistence backend lacks required capabilities: #{missing.keys.join(", ")}"
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
end
|
data/lib/phronomy/recovery.rb
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
# Small private Recovery semantic/primitives layer shared by Agent and
|
|
5
|
-
# Workflow recovery. Entity-specific orchestration remains outside this module.
|
|
6
|
-
# @api private
|
|
7
|
-
module Recovery
|
|
8
|
-
RESUMABLE = :resumable
|
|
9
|
-
RECONCILABLE = :reconcilable
|
|
10
|
-
RESOLUTION_REQUIRED = :resolution_required
|
|
11
|
-
DISPOSITIONS = [RESUMABLE, RECONCILABLE, RESOLUTION_REQUIRED].freeze
|
|
12
|
-
|
|
13
|
-
ALLOWED_OUTCOMES = %i[succeeded failed not_performed].freeze
|
|
14
|
-
# Internal shorthand used by the Agent Recovery implementation. This is not
|
|
15
|
-
# a public API constant; both names refer to the same frozen value.
|
|
16
|
-
OUTCOMES = ALLOWED_OUTCOMES
|
|
17
|
-
|
|
18
|
-
MISSING = Object.new.freeze
|
|
19
|
-
|
|
20
|
-
Classification = Data.define(
|
|
21
|
-
:disposition, :reason, :subject, :allowed_outcomes, :facts
|
|
22
|
-
) do
|
|
23
|
-
def initialize(
|
|
24
|
-
disposition:,
|
|
25
|
-
reason:,
|
|
26
|
-
subject: nil,
|
|
27
|
-
allowed_outcomes: [],
|
|
28
|
-
facts: {}
|
|
29
|
-
)
|
|
30
|
-
normalized = disposition.to_sym
|
|
31
|
-
unless Phronomy::Recovery::DISPOSITIONS.include?(normalized)
|
|
32
|
-
raise ArgumentError, "unknown Recovery disposition: #{disposition.inspect}"
|
|
33
|
-
end
|
|
34
|
-
super(
|
|
35
|
-
disposition: normalized,
|
|
36
|
-
reason: reason.to_sym,
|
|
37
|
-
subject: subject && Phronomy::Recovery.normalize_subject(subject),
|
|
38
|
-
allowed_outcomes: Array(allowed_outcomes).map(&:to_sym).freeze,
|
|
39
|
-
facts: Phronomy::Agent::Immutable.copy(facts || {})
|
|
40
|
-
)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
module_function
|
|
45
|
-
|
|
46
|
-
def normalize_outcome(outcome)
|
|
47
|
-
normalized = outcome.to_sym
|
|
48
|
-
unless OUTCOMES.include?(normalized)
|
|
49
|
-
raise ArgumentError, "unsupported Recovery outcome: #{outcome.inspect}"
|
|
50
|
-
end
|
|
51
|
-
normalized
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def validate_resolution_material!(outcome:, result_present:, error_present:)
|
|
55
|
-
normalized = normalize_outcome(outcome)
|
|
56
|
-
case normalized
|
|
57
|
-
when :succeeded
|
|
58
|
-
raise ArgumentError, "Recovery :succeeded requires result:" unless result_present
|
|
59
|
-
raise ArgumentError, "Recovery :succeeded forbids error:" if error_present
|
|
60
|
-
when :failed
|
|
61
|
-
raise ArgumentError, "Recovery :failed requires error:" unless error_present
|
|
62
|
-
raise ArgumentError, "Recovery :failed forbids result:" if result_present
|
|
63
|
-
when :not_performed
|
|
64
|
-
if result_present || error_present
|
|
65
|
-
raise ArgumentError, "Recovery :not_performed accepts neither result: nor error:"
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
true
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def normalize_subject(subject)
|
|
72
|
-
hash = subject.to_h.transform_keys(&:to_sym)
|
|
73
|
-
type = hash.fetch(:type).to_sym
|
|
74
|
-
normalized = {type: type}
|
|
75
|
-
case type
|
|
76
|
-
when :llm_call
|
|
77
|
-
normalized[:llm_call_id] = required_id(hash, :llm_call_id)
|
|
78
|
-
when :tool_invocation
|
|
79
|
-
normalized[:tool_invocation_id] = required_id(hash, :tool_invocation_id)
|
|
80
|
-
when :persistence_operation
|
|
81
|
-
# Persistence operation references are structural only. Do not introduce
|
|
82
|
-
# a generic durable operation/recovery identity here.
|
|
83
|
-
normalized[:entity] = hash.fetch(:entity).to_sym
|
|
84
|
-
else
|
|
85
|
-
raise ArgumentError, "unsupported Recovery subject type: #{type.inspect}"
|
|
86
|
-
end
|
|
87
|
-
normalized.freeze
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def subject_key(subject)
|
|
91
|
-
normalized = normalize_subject(subject)
|
|
92
|
-
case normalized.fetch(:type)
|
|
93
|
-
when :llm_call
|
|
94
|
-
"llm_call:#{normalized.fetch(:llm_call_id)}"
|
|
95
|
-
when :tool_invocation
|
|
96
|
-
"tool_invocation:#{normalized.fetch(:tool_invocation_id)}"
|
|
97
|
-
when :persistence_operation
|
|
98
|
-
"persistence_operation:#{normalized.fetch(:entity)}"
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def subject_equal?(left, right)
|
|
103
|
-
normalize_subject(left) == normalize_subject(right)
|
|
104
|
-
rescue ArgumentError, KeyError
|
|
105
|
-
false
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Compare authoritative current state with a known pre/post revision pair.
|
|
109
|
-
# This is deliberately small: callers still validate entity-specific content.
|
|
110
|
-
def compare_revisions(current_revision:, expected_pre_revision:, intended_post_revision:)
|
|
111
|
-
current = current_revision
|
|
112
|
-
return :post_state if current == intended_post_revision
|
|
113
|
-
return :pre_state if current == expected_pre_revision
|
|
114
|
-
|
|
115
|
-
:conflict
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Generic F1 helper for revisioned snapshot repositories such as Workflow.
|
|
119
|
-
# Some repositories return a newly allocated revision only after save, so
|
|
120
|
-
# callers may not know the intended post revision in advance. In that case
|
|
121
|
-
# the intended snapshot plus a revision different from the expected pre
|
|
122
|
-
# revision identifies the post-state.
|
|
123
|
-
def compare_revisioned_snapshot(
|
|
124
|
-
record:,
|
|
125
|
-
expected_pre_revision:,
|
|
126
|
-
intended_snapshot:,
|
|
127
|
-
intended_post_revision: nil
|
|
128
|
-
)
|
|
129
|
-
return :pre_state if record.nil? && expected_pre_revision.nil?
|
|
130
|
-
return :conflict if record.nil?
|
|
131
|
-
|
|
132
|
-
revision = fetch_value(record, :revision)
|
|
133
|
-
snapshot = fetch_value(record, :snapshot)
|
|
134
|
-
normalized_snapshot = normalize_value(snapshot)
|
|
135
|
-
normalized_intended = normalize_value(intended_snapshot)
|
|
136
|
-
|
|
137
|
-
if intended_post_revision
|
|
138
|
-
if revision == intended_post_revision &&
|
|
139
|
-
normalized_snapshot == normalized_intended
|
|
140
|
-
return :post_state
|
|
141
|
-
end
|
|
142
|
-
elsif normalized_snapshot == normalized_intended &&
|
|
143
|
-
revision != expected_pre_revision
|
|
144
|
-
return :post_state
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
return :pre_state if revision == expected_pre_revision
|
|
148
|
-
|
|
149
|
-
:conflict
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def normalize_value(value)
|
|
153
|
-
case value
|
|
154
|
-
when Hash
|
|
155
|
-
value.each_with_object({}) do |(key, child), result|
|
|
156
|
-
result[key.to_s] = normalize_value(child)
|
|
157
|
-
end
|
|
158
|
-
when Array
|
|
159
|
-
value.map { |child| normalize_value(child) }
|
|
160
|
-
when Symbol
|
|
161
|
-
value.to_s
|
|
162
|
-
else
|
|
163
|
-
value
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def fetch_value(record, key)
|
|
168
|
-
return nil unless record
|
|
169
|
-
return record.public_send(key) if record.respond_to?(key)
|
|
170
|
-
return record[key] if record.respond_to?(:key?) && record.key?(key)
|
|
171
|
-
string_key = key.to_s
|
|
172
|
-
return record[string_key] if record.respond_to?(:key?) && record.key?(string_key)
|
|
173
|
-
|
|
174
|
-
nil
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def required_id(hash, key)
|
|
178
|
-
value = hash.fetch(key)
|
|
179
|
-
text = value.to_s
|
|
180
|
-
raise ArgumentError, "#{key} must not be empty" if text.empty?
|
|
181
|
-
|
|
182
|
-
text.freeze
|
|
183
|
-
end
|
|
184
|
-
private_class_method :required_id
|
|
185
|
-
end
|
|
186
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Patches for upstream ruby_llm bugs that have not yet been released.
|
|
4
|
-
# Remove each patch once the fix is available in a published gem version.
|
|
5
|
-
|
|
6
|
-
# Guard: apply monkey-patches only to affected ruby_llm versions so that
|
|
7
|
-
# upgrading the gem does not silently keep dead overrides in place.
|
|
8
|
-
if Gem::Version.new(RubyLLM::VERSION) <= Gem::Version.new("1.15.0")
|
|
9
|
-
module RubyLLM
|
|
10
|
-
module Streaming
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
# Upstream ruby_llm <= 1.15.0 assumes the SSE error chunk always has two
|
|
14
|
-
# lines ("event: error\ndata: {...}") and uses a fixed index [1], which
|
|
15
|
-
# raises NoMethodError when some providers (e.g. Qwen) return a single-line
|
|
16
|
-
# chunk ("data: {...}"). This patch finds the data line by content instead.
|
|
17
|
-
def handle_error_chunk(chunk, env)
|
|
18
|
-
data_line = chunk.split("\n").find { |l| l.start_with?("data: ") } || chunk.split("\n")[0]
|
|
19
|
-
error_data = data_line.delete_prefix("data: ")
|
|
20
|
-
parse_error_from_json(error_data, env, "Failed to parse error chunk")
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
data/lib/phronomy/runnable.rb
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
# Base interface for executable graph components.
|
|
5
|
-
# Provides invoke / stream / batch.
|
|
6
|
-
module Runnable
|
|
7
|
-
# Synchronous execution. Must be overridden in subclasses.
|
|
8
|
-
def invoke(input, config: {})
|
|
9
|
-
raise NotImplementedError, "#{self.class}#invoke is not implemented"
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Streaming execution. Default yields the invoke result as a single chunk.
|
|
13
|
-
def stream(input, config: {}, &block)
|
|
14
|
-
result = invoke(input, config: config)
|
|
15
|
-
yield result if block
|
|
16
|
-
result
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Batch execution. Default calls invoke sequentially.
|
|
20
|
-
def batch(inputs, config: {})
|
|
21
|
-
inputs.map { |input| invoke(input, config: config) }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Convenience wrapper that delegates to the global tracer.
|
|
25
|
-
# Yields a span; the block must return [result, usage] where usage is a
|
|
26
|
-
# Phronomy::TokenUsage or nil. Returns only the result value.
|
|
27
|
-
#
|
|
28
|
-
# When +trace_pii+ is disabled, both the input and the output (LLM response,
|
|
29
|
-
# tool result) are replaced with the literal string "[REDACTED]" before being
|
|
30
|
-
# forwarded to the tracing backend. The actual result is still returned to
|
|
31
|
-
# the caller — only the copy sent to the tracer is redacted.
|
|
32
|
-
#
|
|
33
|
-
# @example
|
|
34
|
-
# trace("my_chain", input: input) { [invoke(input), nil] }
|
|
35
|
-
# @api public
|
|
36
|
-
def trace(name, input: nil, **meta, &block)
|
|
37
|
-
traced_input = Phronomy.configuration.trace_pii ? input : "[REDACTED]"
|
|
38
|
-
|
|
39
|
-
if Phronomy.configuration.trace_pii
|
|
40
|
-
# PII recording is allowed: pass through unchanged.
|
|
41
|
-
Phronomy.configuration.tracer.trace(name, input: traced_input, **meta, &block)
|
|
42
|
-
else
|
|
43
|
-
# Redact both input (above) and output before forwarding to the tracer.
|
|
44
|
-
# Capture the real result so callers receive the unredacted value.
|
|
45
|
-
real_result = nil
|
|
46
|
-
Phronomy.configuration.tracer.trace(name, input: traced_input, **meta) do |span|
|
|
47
|
-
real_result, usage = block.call(span)
|
|
48
|
-
["[REDACTED]", usage]
|
|
49
|
-
end
|
|
50
|
-
real_result
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
data/lib/phronomy/token_usage.rb
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
# Immutable value object that holds token usage counts for a single LLM call
|
|
5
|
-
# or an accumulated total across multiple calls.
|
|
6
|
-
#
|
|
7
|
-
# Fields mirror the values reported by RubyLLM::Tokens:
|
|
8
|
-
#
|
|
9
|
-
# input - tokens consumed by the input (prompt)
|
|
10
|
-
# output - tokens produced by the model (completion)
|
|
11
|
-
# cached - input tokens served from the provider's prompt cache
|
|
12
|
-
# cache_creation - input tokens written into the prompt cache (Anthropic)
|
|
13
|
-
#
|
|
14
|
-
# All fields are Integer or nil (nil means the provider did not report that
|
|
15
|
-
# value for this call).
|
|
16
|
-
#
|
|
17
|
-
# @example Accumulate usage across a ReAct loop
|
|
18
|
-
# total = Phronomy::TokenUsage.zero
|
|
19
|
-
# chat.messages.each do |msg|
|
|
20
|
-
# total += Phronomy::TokenUsage.from_tokens(msg.tokens)
|
|
21
|
-
# end
|
|
22
|
-
class TokenUsage
|
|
23
|
-
attr_reader :input, :output, :cached, :cache_creation
|
|
24
|
-
|
|
25
|
-
def initialize(input: nil, output: nil, cached: nil, cache_creation: nil)
|
|
26
|
-
@input = input
|
|
27
|
-
@output = output
|
|
28
|
-
@cached = cached
|
|
29
|
-
@cache_creation = cache_creation
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Returns a zero-valued TokenUsage suitable as an accumulator seed.
|
|
33
|
-
def self.zero
|
|
34
|
-
new(input: 0, output: 0, cached: 0, cache_creation: 0)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Builds a TokenUsage from a RubyLLM::Tokens object. Returns zero
|
|
38
|
-
# when tokens is nil (provider did not report usage).
|
|
39
|
-
# nil fields within the tokens object are coerced to 0.
|
|
40
|
-
def self.from_tokens(tokens)
|
|
41
|
-
return zero if tokens.nil?
|
|
42
|
-
|
|
43
|
-
new(
|
|
44
|
-
input: tokens.input || 0,
|
|
45
|
-
output: tokens.output || 0,
|
|
46
|
-
cached: tokens.cached || 0,
|
|
47
|
-
cache_creation: tokens.cache_creation || 0
|
|
48
|
-
)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Adds two TokenUsage instances. nil fields are treated as 0 so that
|
|
52
|
-
# partially-reported usage accumulates correctly.
|
|
53
|
-
# When other is nil, returns self unchanged.
|
|
54
|
-
def +(other)
|
|
55
|
-
return self if other.nil?
|
|
56
|
-
|
|
57
|
-
self.class.new(
|
|
58
|
-
input: _add(input, other.input),
|
|
59
|
-
output: _add(output, other.output),
|
|
60
|
-
cached: _add(cached, other.cached),
|
|
61
|
-
cache_creation: _add(cache_creation, other.cache_creation)
|
|
62
|
-
)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def ==(other)
|
|
66
|
-
other.is_a?(TokenUsage) &&
|
|
67
|
-
input == other.input &&
|
|
68
|
-
output == other.output &&
|
|
69
|
-
cached == other.cached &&
|
|
70
|
-
cache_creation == other.cache_creation
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def to_h
|
|
74
|
-
{input: input, output: output, cached: cached, cache_creation: cache_creation}
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
private
|
|
78
|
-
|
|
79
|
-
def _add(a, b)
|
|
80
|
-
return nil if a.nil? && b.nil?
|
|
81
|
-
|
|
82
|
-
(a || 0) + (b || 0)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module VectorStore
|
|
5
|
-
module Embeddings
|
|
6
|
-
# Public extension SPI for embedding adapters.
|
|
7
|
-
#
|
|
8
|
-
# Concrete implementations override {#embed}. Phronomy owns the async
|
|
9
|
-
# bridge: {#embed_async} routes the synchronous implementation through the
|
|
10
|
-
# bounded OffloadPool and returns a {Phronomy::TaskResult}.
|
|
11
|
-
#
|
|
12
|
-
# @api public
|
|
13
|
-
class Base
|
|
14
|
-
# Embed the given text and return a vector representation.
|
|
15
|
-
#
|
|
16
|
-
# @param text [String] the text to embed
|
|
17
|
-
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
|
|
18
|
-
# @return [Array<Float>] the embedding vector
|
|
19
|
-
# @api public
|
|
20
|
-
def embed(text, cancellation_token = nil)
|
|
21
|
-
cancellation_token&.raise_if_cancelled!
|
|
22
|
-
raise NotImplementedError, "#{self.class}#embed is not implemented"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Submits an {#embed} call to {OffloadPool}.
|
|
26
|
-
#
|
|
27
|
-
# @param text [String]
|
|
28
|
-
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
|
|
29
|
-
# @param timeout [Numeric, nil] operation-wide submit timeout
|
|
30
|
-
# @return [Phronomy::TaskResult]
|
|
31
|
-
# @api public
|
|
32
|
-
def embed_async(text, cancellation_token = nil, timeout: nil)
|
|
33
|
-
Phronomy::Runtime.instance.offload.submit(
|
|
34
|
-
timeout: timeout,
|
|
35
|
-
cancellation_token: cancellation_token,
|
|
36
|
-
on_full: :raise
|
|
37
|
-
) do
|
|
38
|
-
embed(text, cancellation_token)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|