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
|
@@ -19,9 +19,9 @@ module Phronomy
|
|
|
19
19
|
|
|
20
20
|
def start(name, input: nil, **metadata)
|
|
21
21
|
trace_pii = false
|
|
22
|
-
|
|
23
|
-
trace_pii = !!
|
|
24
|
-
tracer =
|
|
22
|
+
settings = Settings.current
|
|
23
|
+
trace_pii = !!settings.trace_pii
|
|
24
|
+
tracer = settings.tracer
|
|
25
25
|
span = tracer.start_span(
|
|
26
26
|
name,
|
|
27
27
|
input: traced_payload(input, trace_pii: trace_pii),
|
|
@@ -47,7 +47,7 @@ module Phronomy
|
|
|
47
47
|
error: traced_error(error, trace_pii: handle.trace_pii)
|
|
48
48
|
}.freeze
|
|
49
49
|
|
|
50
|
-
if Phronomy::
|
|
50
|
+
if Phronomy::WaitPolicy.blocking_forbidden?
|
|
51
51
|
submit_finish(handle, attributes)
|
|
52
52
|
else
|
|
53
53
|
finish_now(handle, attributes)
|
|
@@ -130,8 +130,13 @@ module Phronomy
|
|
|
130
130
|
private_class_method :task_usage
|
|
131
131
|
|
|
132
132
|
def submit_finish(handle, attributes)
|
|
133
|
-
Phronomy::
|
|
133
|
+
Phronomy::Blocking.call_async do
|
|
134
134
|
finish_now(handle, attributes)
|
|
135
|
+
end.on_complete do |_value, error|
|
|
136
|
+
if error
|
|
137
|
+
report_failure(:finish, handle.name, error,
|
|
138
|
+
include_message: handle.trace_pii)
|
|
139
|
+
end
|
|
135
140
|
end
|
|
136
141
|
nil
|
|
137
142
|
rescue => error
|
|
@@ -164,7 +169,7 @@ module Phronomy
|
|
|
164
169
|
message =
|
|
165
170
|
"[Phronomy] automatic tracing #{stage} failed for " \
|
|
166
171
|
"#{name}: #{error.class}#{detail}"
|
|
167
|
-
logger = Phronomy.
|
|
172
|
+
logger = Phronomy::RuntimeSettings.current.logger
|
|
168
173
|
logger ? logger.warn(message) : Kernel.warn(message)
|
|
169
174
|
nil
|
|
170
175
|
rescue
|
|
@@ -34,15 +34,44 @@ module Phronomy
|
|
|
34
34
|
# @return [Object] the block's return value
|
|
35
35
|
# @api public
|
|
36
36
|
def trace(name, input: nil, **meta)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
Base.trace_with(self, name, attributes: {input: input, **meta}) { |span| yield span }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Use the public start/finish SPI, including duck-typed tracers.
|
|
41
|
+
# Start failures do not finish; finish is attempted once after execution.
|
|
42
|
+
# A secondary finish/reporting failure cannot replace the block's error.
|
|
43
|
+
# Set record_output: false for metadata-only operations.
|
|
44
|
+
# @api public
|
|
45
|
+
def self.trace_with(tracer, name, record_output: true, attributes: {})
|
|
46
|
+
span = tracer.start_span(name, **attributes)
|
|
47
|
+
begin
|
|
48
|
+
result, usage = yield span
|
|
49
|
+
rescue => primary
|
|
50
|
+
begin
|
|
51
|
+
tracer.finish_span(span, error: primary)
|
|
52
|
+
rescue => secondary
|
|
53
|
+
report_secondary(secondary)
|
|
54
|
+
end
|
|
55
|
+
raise
|
|
56
|
+
end
|
|
57
|
+
if record_output
|
|
58
|
+
tracer.finish_span(span, output: result, usage: usage)
|
|
59
|
+
else
|
|
60
|
+
tracer.finish_span(span)
|
|
61
|
+
end
|
|
40
62
|
result
|
|
41
|
-
rescue => e
|
|
42
|
-
finish_span(span, error: e)
|
|
43
|
-
raise
|
|
44
63
|
end
|
|
45
64
|
|
|
65
|
+
def self.report_secondary(error)
|
|
66
|
+
Phronomy::RuntimeSettings.current.logger&.warn(
|
|
67
|
+
"[Phronomy::Tracing] secondary finish failure: #{error.class}: #{error.message}"
|
|
68
|
+
)
|
|
69
|
+
rescue
|
|
70
|
+
# Diagnostics are best effort; preserve the primary business failure.
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
private_class_method :report_secondary
|
|
74
|
+
|
|
46
75
|
# Start a new span. Must return an object that will be passed to #finish_span.
|
|
47
76
|
#
|
|
48
77
|
# @param name [String]
|
|
@@ -57,7 +86,7 @@ module Phronomy
|
|
|
57
86
|
#
|
|
58
87
|
# @param span [Object] the span returned by #start_span
|
|
59
88
|
# @param output [Object, nil] successful output value
|
|
60
|
-
# @param usage [Phronomy::TokenUsage, nil] token usage for this span
|
|
89
|
+
# @param usage [Phronomy::LLMAdapter::TokenUsage, nil] token usage for this span
|
|
61
90
|
# @param error [Exception, nil] exception if the block raised
|
|
62
91
|
# @api public
|
|
63
92
|
def finish_span(span, output: nil, usage: nil, error: nil)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Tracing
|
|
5
|
+
module Observation
|
|
6
|
+
# Convenience wrapper that delegates to the global tracer.
|
|
7
|
+
# Yields a span; the block must return [result, usage] where usage is a
|
|
8
|
+
# Phronomy::LLMAdapter::TokenUsage or nil. Returns only the result value.
|
|
9
|
+
#
|
|
10
|
+
# When +trace_pii+ is disabled, both the input and the output (LLM response,
|
|
11
|
+
# tool result) are replaced with the literal string "[REDACTED]" before being
|
|
12
|
+
# forwarded to the tracing backend. The actual result is still returned to
|
|
13
|
+
# the caller — only the copy sent to the tracer is redacted.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# trace("my_chain", input: input) { [invoke(input), nil] }
|
|
17
|
+
# @api public
|
|
18
|
+
def self.trace(name, input: nil, **meta, &block)
|
|
19
|
+
traced_input = Settings.current.trace_pii ? input : "[REDACTED]"
|
|
20
|
+
|
|
21
|
+
if Settings.current.trace_pii
|
|
22
|
+
# PII recording is allowed: pass through unchanged.
|
|
23
|
+
Settings.current.tracer.trace(name, input: traced_input, **meta, &block)
|
|
24
|
+
else
|
|
25
|
+
# Redact both input (above) and output before forwarding to the tracer.
|
|
26
|
+
# Capture the real result so callers receive the unredacted value.
|
|
27
|
+
real_result = nil
|
|
28
|
+
Settings.current.tracer.trace(name, input: traced_input, **meta) do |span|
|
|
29
|
+
real_result, usage = block.call(span)
|
|
30
|
+
["[REDACTED]", usage]
|
|
31
|
+
end
|
|
32
|
+
real_result
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -53,38 +53,13 @@ module Phronomy
|
|
|
53
53
|
span.finish
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
# automatically inherit the current span as their parent, establishing the
|
|
59
|
-
# correct parent/child hierarchy in the trace tree.
|
|
60
|
-
#
|
|
61
|
-
# @param name [String] span name
|
|
62
|
-
# @param input [Object, nil] input to record as a span attribute
|
|
63
|
-
# @param meta [Hash] additional metadata (e.g. task_id, user_id)
|
|
64
|
-
# @yield [span] the active OTel span
|
|
65
|
-
# @return [Object] the block's return value
|
|
56
|
+
# Preserve the backend context while sharing C15's exactly-once finish
|
|
57
|
+
# and primary-error protection. Context affinity remains backend-owned.
|
|
66
58
|
# @api private
|
|
67
59
|
def trace(name, input: nil, **meta)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
meta.each { |k, v| attrs["phronomy.#{k}"] = v.to_s unless v.nil? }
|
|
71
|
-
|
|
72
|
-
result = nil
|
|
73
|
-
@otel_tracer.in_span(name, attributes: attrs) do |span|
|
|
74
|
-
result, usage = yield span
|
|
75
|
-
span.set_attribute("phronomy.output", result.to_s) if result
|
|
76
|
-
if usage
|
|
77
|
-
span.set_attribute("llm.usage.input_tokens", usage.input)
|
|
78
|
-
span.set_attribute("llm.usage.output_tokens", usage.output)
|
|
79
|
-
total = (usage.input || 0) + (usage.output || 0)
|
|
80
|
-
span.set_attribute("llm.usage.total_tokens", total)
|
|
81
|
-
end
|
|
82
|
-
rescue => e
|
|
83
|
-
span.record_exception(e)
|
|
84
|
-
span.status = OpenTelemetry::Trace::Status.error(e.message)
|
|
85
|
-
raise
|
|
60
|
+
Base.trace_with(self, name, attributes: {input: input, **meta.compact}) do |span|
|
|
61
|
+
OpenTelemetry::Trace.with_span(span) { yield span }
|
|
86
62
|
end
|
|
87
|
-
result
|
|
88
63
|
end
|
|
89
64
|
end
|
|
90
65
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../common/configuration_error"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Tracing
|
|
7
|
+
# Tracing owns recording options; application composition selects the tracer.
|
|
8
|
+
# No concrete backend, execution resource or application facade belongs here.
|
|
9
|
+
# @api private
|
|
10
|
+
class Settings
|
|
11
|
+
def self.install_provider(&provider)
|
|
12
|
+
raise ArgumentError, "Tracing settings provider requires a block" unless provider
|
|
13
|
+
|
|
14
|
+
@provider = provider
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Follow reset and scoped restoration only when a consumer already reads.
|
|
19
|
+
def self.current
|
|
20
|
+
settings = @provider&.call
|
|
21
|
+
unless settings.is_a?(Settings)
|
|
22
|
+
raise ConfigurationError, "Tracing settings provider must return Tracing::Settings"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
settings
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_accessor :tracer, :trace_pii
|
|
29
|
+
|
|
30
|
+
def initialize(tracer:)
|
|
31
|
+
@tracer = tracer
|
|
32
|
+
@trace_pii = false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -2,21 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module VectorStore
|
|
5
|
-
# Framework-owned
|
|
5
|
+
# Framework-owned asynchronous execution of a synchronous vector backend.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
7
|
+
# Backend authors implement only Base's synchronous operations. Applications
|
|
8
|
+
# use this client for asynchronous calls. Admission never waits for queue
|
|
9
|
+
# capacity; accepted calls return the pool's original TaskResult. Timeout
|
|
10
|
+
# includes queue wait and settles the result without interrupting running I/O.
|
|
11
|
+
# Construction does not start Runtime; the current default pool is resolved
|
|
12
|
+
# for each operation. An injected pool remains owned by its caller.
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
# @api public
|
|
15
|
+
class AsyncClient
|
|
16
|
+
# @param backend [VectorStore::Base] synchronous vector store
|
|
17
|
+
# @param pool [Concurrency::OffloadPool, nil] optional execution pool
|
|
18
|
+
# @api public
|
|
19
|
+
def initialize(backend:, pool: nil)
|
|
20
|
+
@backend = backend
|
|
21
|
+
@pool = pool
|
|
22
|
+
end
|
|
23
|
+
|
|
20
24
|
# Async variant of {VectorStore::Base#add}.
|
|
21
25
|
#
|
|
22
26
|
# @param id [String]
|
|
@@ -27,12 +31,11 @@ module Phronomy
|
|
|
27
31
|
# @return [Phronomy::TaskResult]
|
|
28
32
|
# @api public
|
|
29
33
|
def add_async(id:, embedding:, metadata: {}, cancellation_token: nil, timeout: nil)
|
|
30
|
-
Phronomy::
|
|
34
|
+
Phronomy::Execution.submit(pool: default_pool,
|
|
31
35
|
timeout: timeout,
|
|
32
36
|
cancellation_token: cancellation_token,
|
|
33
|
-
on_full: :raise
|
|
34
|
-
|
|
35
|
-
add(id: id, embedding: embedding, metadata: metadata, cancellation_token: cancellation_token)
|
|
37
|
+
on_full: :raise) do
|
|
38
|
+
@backend.add(id: id, embedding: embedding, metadata: metadata, cancellation_token: cancellation_token)
|
|
36
39
|
end
|
|
37
40
|
end
|
|
38
41
|
|
|
@@ -45,12 +48,11 @@ module Phronomy
|
|
|
45
48
|
# @return [Phronomy::TaskResult]
|
|
46
49
|
# @api public
|
|
47
50
|
def search_async(query_embedding:, k: 5, cancellation_token: nil, timeout: nil)
|
|
48
|
-
Phronomy::
|
|
51
|
+
Phronomy::Execution.submit(pool: default_pool,
|
|
49
52
|
timeout: timeout,
|
|
50
53
|
cancellation_token: cancellation_token,
|
|
51
|
-
on_full: :raise
|
|
52
|
-
|
|
53
|
-
search(query_embedding: query_embedding, k: k, cancellation_token: cancellation_token)
|
|
54
|
+
on_full: :raise) do
|
|
55
|
+
@backend.search(query_embedding: query_embedding, k: k, cancellation_token: cancellation_token)
|
|
54
56
|
end
|
|
55
57
|
end
|
|
56
58
|
|
|
@@ -62,12 +64,11 @@ module Phronomy
|
|
|
62
64
|
# @return [Phronomy::TaskResult]
|
|
63
65
|
# @api public
|
|
64
66
|
def remove_async(id:, cancellation_token: nil, timeout: nil)
|
|
65
|
-
Phronomy::
|
|
67
|
+
Phronomy::Execution.submit(pool: default_pool,
|
|
66
68
|
timeout: timeout,
|
|
67
69
|
cancellation_token: cancellation_token,
|
|
68
|
-
on_full: :raise
|
|
69
|
-
|
|
70
|
-
remove(id: id)
|
|
70
|
+
on_full: :raise) do
|
|
71
|
+
@backend.remove(id: id)
|
|
71
72
|
end
|
|
72
73
|
end
|
|
73
74
|
|
|
@@ -78,14 +79,19 @@ module Phronomy
|
|
|
78
79
|
# @return [Phronomy::TaskResult]
|
|
79
80
|
# @api public
|
|
80
81
|
def clear_async(cancellation_token: nil, timeout: nil)
|
|
81
|
-
Phronomy::
|
|
82
|
+
Phronomy::Execution.submit(pool: default_pool,
|
|
82
83
|
timeout: timeout,
|
|
83
84
|
cancellation_token: cancellation_token,
|
|
84
|
-
on_full: :raise
|
|
85
|
-
|
|
86
|
-
clear
|
|
85
|
+
on_full: :raise) do
|
|
86
|
+
@backend.clear
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def default_pool
|
|
93
|
+
@pool
|
|
94
|
+
end
|
|
89
95
|
end
|
|
90
96
|
end
|
|
91
97
|
end
|
|
@@ -22,16 +22,16 @@ module Phronomy
|
|
|
22
22
|
@expected_dimension = dimension
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
protected
|
|
26
|
+
|
|
25
27
|
# @param id [String]
|
|
26
28
|
# @param embedding [Array<Float>]
|
|
27
29
|
# @param metadata [Hash]
|
|
28
30
|
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
|
|
29
31
|
# @api public
|
|
30
|
-
def
|
|
31
|
-
cancellation_token&.raise_if_cancelled!
|
|
32
|
+
def perform_add(id:, embedding:, metadata: {}, cancellation_token: nil)
|
|
32
33
|
# Establish expected dimension on first add, then validate.
|
|
33
34
|
@expected_dimension ||= embedding.size
|
|
34
|
-
validate_embedding_dimension!(embedding, @expected_dimension)
|
|
35
35
|
@documents[id] = {embedding: embedding, metadata: metadata}
|
|
36
36
|
self
|
|
37
37
|
end
|
|
@@ -46,11 +46,8 @@ module Phronomy
|
|
|
46
46
|
# (key always present); -r.fetch(:score) vs -r[:score] (key always present); snapshot = @documents
|
|
47
47
|
# vs .dup is equivalent in single-threaded tests (GVL makes Hash#dup atomic, no behaviour
|
|
48
48
|
# difference under test isolation)
|
|
49
|
-
def
|
|
50
|
-
cancellation_token&.raise_if_cancelled!
|
|
51
|
-
k = validate_k!(k)
|
|
49
|
+
def perform_search(query_embedding:, k: 5, cancellation_token: nil)
|
|
52
50
|
# search never establishes dimension; validate only when dimension is known.
|
|
53
|
-
validate_embedding_dimension!(query_embedding, @expected_dimension)
|
|
54
51
|
# Take an atomic snapshot before iterating. Hash#dup is a C-level
|
|
55
52
|
# call that completes without releasing the GVL, so it is atomic with
|
|
56
53
|
# respect to any other Ruby thread. Iterating the copy instead of
|
|
@@ -64,22 +61,27 @@ module Phronomy
|
|
|
64
61
|
results.sort_by { |r| -r[:score] }.first(k)
|
|
65
62
|
end
|
|
66
63
|
|
|
67
|
-
def
|
|
64
|
+
def perform_remove(id:)
|
|
68
65
|
@documents.delete(id)
|
|
69
66
|
self
|
|
70
67
|
end
|
|
71
68
|
|
|
72
|
-
def
|
|
69
|
+
def perform_clear
|
|
73
70
|
@documents.clear
|
|
74
71
|
self
|
|
75
72
|
end
|
|
76
73
|
|
|
77
74
|
# @return [Integer] number of documents stored
|
|
78
75
|
# @api public
|
|
79
|
-
def
|
|
76
|
+
def perform_size
|
|
80
77
|
@documents.size
|
|
81
78
|
end
|
|
82
79
|
|
|
80
|
+
# @api public
|
|
81
|
+
def embedding_dimension
|
|
82
|
+
@expected_dimension
|
|
83
|
+
end
|
|
84
|
+
|
|
83
85
|
private
|
|
84
86
|
|
|
85
87
|
# mutant:disable - empty-vector early-return condition variants (if false, if nil, if a.empty?,
|
|
@@ -35,14 +35,14 @@ module Phronomy
|
|
|
35
35
|
@dimension = dimension
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
protected
|
|
39
|
+
|
|
38
40
|
# @param id [String]
|
|
39
41
|
# @param embedding [Array<Float>]
|
|
40
42
|
# @param metadata [Hash]
|
|
41
43
|
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
|
|
42
44
|
# @api public
|
|
43
|
-
def
|
|
44
|
-
cancellation_token&.raise_if_cancelled!
|
|
45
|
-
validate_embedding_dimension!(embedding, @dimension)
|
|
45
|
+
def perform_add(id:, embedding:, metadata: {}, cancellation_token: nil)
|
|
46
46
|
@model_class.upsert(
|
|
47
47
|
{id: id, embedding: safe_vector(embedding), metadata: metadata.to_json},
|
|
48
48
|
unique_by: :id
|
|
@@ -55,10 +55,7 @@ module Phronomy
|
|
|
55
55
|
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
|
|
56
56
|
# @return [Array<Hash>] sorted by descending similarity score
|
|
57
57
|
# @api public
|
|
58
|
-
def
|
|
59
|
-
cancellation_token&.raise_if_cancelled!
|
|
60
|
-
k_safe = validate_k!(k)
|
|
61
|
-
validate_embedding_dimension!(query_embedding, @dimension)
|
|
58
|
+
def perform_search(query_embedding:, k: 5, cancellation_token: nil)
|
|
62
59
|
vec = safe_vector_literal(query_embedding)
|
|
63
60
|
conn = @model_class.connection
|
|
64
61
|
quoted_vec = "#{conn.quote(vec)}::vector"
|
|
@@ -66,48 +63,79 @@ module Phronomy
|
|
|
66
63
|
@model_class
|
|
67
64
|
.select("id, metadata, 1 - (embedding <=> #{quoted_vec}) AS score")
|
|
68
65
|
.order("embedding <=> #{quoted_vec}")
|
|
69
|
-
.limit(
|
|
66
|
+
.limit(k)
|
|
70
67
|
.map do |r|
|
|
71
68
|
{
|
|
72
|
-
id: r.id
|
|
73
|
-
score: r.score
|
|
69
|
+
id: parse_id(r.id),
|
|
70
|
+
score: parse_score(r.score),
|
|
74
71
|
metadata: parse_metadata(r.metadata)
|
|
75
72
|
}
|
|
76
73
|
end
|
|
77
74
|
end
|
|
78
75
|
|
|
79
|
-
def
|
|
76
|
+
def perform_remove(id:)
|
|
80
77
|
@model_class.where(id: id).delete_all
|
|
81
78
|
self
|
|
82
79
|
end
|
|
83
80
|
|
|
84
|
-
def
|
|
81
|
+
def perform_clear
|
|
85
82
|
@model_class.delete_all
|
|
86
83
|
self
|
|
87
84
|
end
|
|
88
85
|
|
|
89
86
|
# Returns the number of documents in the backing table.
|
|
90
|
-
def
|
|
87
|
+
def perform_size
|
|
91
88
|
@model_class.count
|
|
92
89
|
end
|
|
93
90
|
|
|
91
|
+
# @api public
|
|
92
|
+
def embedding_dimension
|
|
93
|
+
@dimension
|
|
94
|
+
end
|
|
95
|
+
|
|
94
96
|
private
|
|
95
97
|
|
|
98
|
+
def parse_id(raw)
|
|
99
|
+
unless raw.is_a?(String) || raw.is_a?(Integer)
|
|
100
|
+
raise InvalidResultError, "Pgvector returned an invalid document id"
|
|
101
|
+
end
|
|
102
|
+
raw.to_s
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def parse_score(raw)
|
|
106
|
+
valid = (raw.is_a?(String) && raw.match?(/\A[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?\z/)) ||
|
|
107
|
+
(raw.is_a?(Numeric) && raw.real?)
|
|
108
|
+
value = Float(raw, exception: false) if valid
|
|
109
|
+
unless value&.finite?
|
|
110
|
+
raise InvalidResultError, "Pgvector requires a finite numeric score"
|
|
111
|
+
end
|
|
112
|
+
value
|
|
113
|
+
end
|
|
114
|
+
|
|
96
115
|
# Parses a metadata value returned by the pg driver.
|
|
97
116
|
# Handles NULL (nil), already-parsed Hash, and JSON string forms.
|
|
98
117
|
def parse_metadata(raw)
|
|
99
118
|
return {} if raw.nil?
|
|
100
119
|
return symbolize_hash_keys(raw) if raw.is_a?(Hash)
|
|
101
120
|
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
unless raw.is_a?(String)
|
|
122
|
+
raise InvalidResultError, "Pgvector metadata must contain a JSON object"
|
|
123
|
+
end
|
|
124
|
+
parsed = JSON.parse(raw, symbolize_names: true)
|
|
125
|
+
unless parsed.is_a?(Hash)
|
|
126
|
+
raise InvalidResultError, "Pgvector metadata must contain a JSON object"
|
|
127
|
+
end
|
|
128
|
+
parsed
|
|
104
129
|
rescue JSON::ParserError
|
|
105
|
-
|
|
130
|
+
raise InvalidResultError, "Pgvector metadata contains invalid JSON"
|
|
106
131
|
end
|
|
107
132
|
|
|
108
133
|
# Recursively symbolizes keys for an already-parsed Hash.
|
|
109
134
|
def symbolize_hash_keys(hash)
|
|
110
135
|
hash.each_with_object({}) do |(k, v), h|
|
|
136
|
+
unless k.is_a?(String) || k.is_a?(Symbol)
|
|
137
|
+
raise InvalidResultError, "Pgvector metadata contains an invalid object key"
|
|
138
|
+
end
|
|
111
139
|
h[k.to_sym] = v.is_a?(Hash) ? symbolize_hash_keys(v) : v
|
|
112
140
|
end
|
|
113
141
|
end
|