phronomy 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +9 -3
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +8 -3
- data/docs/getting-started.md +9 -6
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +37 -32
- data/lib/phronomy/agent/base.rb +74 -72
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +8 -2
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/tool/tool_executor.rb +58 -0
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/workflow/execution/workflow.rb +7 -7
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
- data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +57 -16
- data/scripts/api_snapshot.rb +35 -15
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -138
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +6 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +1 -99
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
- data/sig/phronomy/tool.rbs +48 -4
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +318 -117
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
module
|
|
4
|
+
module Context
|
|
5
5
|
# ContentStore-backed durable codec boundary for one Provider input manifest.
|
|
6
6
|
# It is intentionally separate from Storage::DurableRecord.
|
|
7
7
|
class LLMInputManifest
|
|
@@ -62,7 +62,7 @@ module Phronomy
|
|
|
62
62
|
label: label
|
|
63
63
|
)
|
|
64
64
|
unless SEGMENT_DELIVERIES.include?(delivery)
|
|
65
|
-
raise Phronomy::
|
|
65
|
+
raise Phronomy::Context::ManifestError,
|
|
66
66
|
"LLMInputManifest segment delivery must be one of #{SEGMENT_DELIVERIES.inspect}, got #{delivery.inspect}"
|
|
67
67
|
end
|
|
68
68
|
tool_call_id = LLMInputManifest.send(
|
|
@@ -87,10 +87,10 @@ module Phronomy
|
|
|
87
87
|
tool_call_id: tool_call_id,
|
|
88
88
|
metadata: metadata
|
|
89
89
|
)
|
|
90
|
-
rescue Phronomy::
|
|
90
|
+
rescue Phronomy::Context::ManifestError
|
|
91
91
|
raise
|
|
92
92
|
rescue => error
|
|
93
|
-
raise Phronomy::
|
|
93
|
+
raise Phronomy::Context::ManifestError,
|
|
94
94
|
"invalid LLMInputManifest segment: #{error.class}: #{error.message}"
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -162,7 +162,7 @@ module Phronomy
|
|
|
162
162
|
label = "LLMInputManifest"
|
|
163
163
|
version = source.fetch("version")
|
|
164
164
|
unless version.is_a?(String) && version == VERSION
|
|
165
|
-
raise Phronomy::
|
|
165
|
+
raise Phronomy::Context::ManifestError,
|
|
166
166
|
"unsupported LLMInputManifest version: #{version.inspect}; " \
|
|
167
167
|
"current version is #{VERSION.inspect}"
|
|
168
168
|
end
|
|
@@ -229,10 +229,10 @@ module Phronomy
|
|
|
229
229
|
adapter_name: adapter_name,
|
|
230
230
|
adapter_version: adapter_version
|
|
231
231
|
)
|
|
232
|
-
rescue Phronomy::
|
|
232
|
+
rescue Phronomy::Context::ManifestError
|
|
233
233
|
raise
|
|
234
234
|
rescue => error
|
|
235
|
-
raise Phronomy::
|
|
235
|
+
raise Phronomy::Context::ManifestError,
|
|
236
236
|
"invalid LLMInputManifest: #{error.class}: #{error.message}"
|
|
237
237
|
end
|
|
238
238
|
|
|
@@ -262,11 +262,11 @@ module Phronomy
|
|
|
262
262
|
|
|
263
263
|
def strict_source!(hash, required:, optional:, label:)
|
|
264
264
|
unless hash.is_a?(Hash)
|
|
265
|
-
raise Phronomy::
|
|
265
|
+
raise Phronomy::Context::ManifestError,
|
|
266
266
|
"#{label} must be a Hash"
|
|
267
267
|
end
|
|
268
268
|
unless hash.keys.all? { |key| key.is_a?(String) }
|
|
269
|
-
raise Phronomy::
|
|
269
|
+
raise Phronomy::Context::ManifestError,
|
|
270
270
|
"#{label} keys must all be String"
|
|
271
271
|
end
|
|
272
272
|
|
|
@@ -277,13 +277,13 @@ module Phronomy
|
|
|
277
277
|
details = []
|
|
278
278
|
details << "missing=#{missing.inspect}" unless missing.empty?
|
|
279
279
|
details << "unknown=#{unknown.inspect}" unless unknown.empty?
|
|
280
|
-
raise Phronomy::
|
|
280
|
+
raise Phronomy::Context::ManifestError,
|
|
281
281
|
"#{label} schema mismatch (#{details.join(", ")})"
|
|
282
282
|
end
|
|
283
283
|
Phronomy::CanonicalJSON.dump(hash)
|
|
284
284
|
hash
|
|
285
285
|
rescue ArgumentError => error
|
|
286
|
-
raise Phronomy::
|
|
286
|
+
raise Phronomy::Context::ManifestError,
|
|
287
287
|
"#{label} is not canonical JSON compatible: #{error.message}"
|
|
288
288
|
end
|
|
289
289
|
|
|
@@ -291,7 +291,7 @@ module Phronomy
|
|
|
291
291
|
value = hash.fetch(key)
|
|
292
292
|
return value if value.is_a?(Integer)
|
|
293
293
|
|
|
294
|
-
raise Phronomy::
|
|
294
|
+
raise Phronomy::Context::ManifestError,
|
|
295
295
|
"#{label} #{key} must be an Integer"
|
|
296
296
|
end
|
|
297
297
|
|
|
@@ -299,7 +299,7 @@ module Phronomy
|
|
|
299
299
|
value = hash.fetch(key)
|
|
300
300
|
return value if value.is_a?(Integer) && value.positive?
|
|
301
301
|
|
|
302
|
-
raise Phronomy::
|
|
302
|
+
raise Phronomy::Context::ManifestError,
|
|
303
303
|
"#{label} #{key} must be a positive Integer"
|
|
304
304
|
end
|
|
305
305
|
|
|
@@ -307,7 +307,7 @@ module Phronomy
|
|
|
307
307
|
value = hash.fetch(key)
|
|
308
308
|
return value if value.is_a?(Integer) && value >= 0
|
|
309
309
|
|
|
310
|
-
raise Phronomy::
|
|
310
|
+
raise Phronomy::Context::ManifestError,
|
|
311
311
|
"#{label} #{key} must be a non-negative Integer"
|
|
312
312
|
end
|
|
313
313
|
|
|
@@ -315,7 +315,7 @@ module Phronomy
|
|
|
315
315
|
value = hash.fetch(key)
|
|
316
316
|
return value if value.is_a?(String) && !value.empty?
|
|
317
317
|
|
|
318
|
-
raise Phronomy::
|
|
318
|
+
raise Phronomy::Context::ManifestError,
|
|
319
319
|
"#{label} #{key} must be a non-empty String"
|
|
320
320
|
end
|
|
321
321
|
|
|
@@ -323,7 +323,7 @@ module Phronomy
|
|
|
323
323
|
value = hash[key]
|
|
324
324
|
return value if value.nil? || value.is_a?(String)
|
|
325
325
|
|
|
326
|
-
raise Phronomy::
|
|
326
|
+
raise Phronomy::Context::ManifestError,
|
|
327
327
|
"#{label} #{key} must be a String or nil"
|
|
328
328
|
end
|
|
329
329
|
|
|
@@ -331,7 +331,7 @@ module Phronomy
|
|
|
331
331
|
value = hash.fetch(key)
|
|
332
332
|
return value if value.is_a?(String) && allowed.include?(value)
|
|
333
333
|
|
|
334
|
-
raise Phronomy::
|
|
334
|
+
raise Phronomy::Context::ManifestError,
|
|
335
335
|
"#{label} #{key} must be one of #{allowed.inspect}"
|
|
336
336
|
end
|
|
337
337
|
|
|
@@ -339,7 +339,7 @@ module Phronomy
|
|
|
339
339
|
value = hash.fetch(key)
|
|
340
340
|
return value if value.is_a?(Array)
|
|
341
341
|
|
|
342
|
-
raise Phronomy::
|
|
342
|
+
raise Phronomy::Context::ManifestError,
|
|
343
343
|
"#{label} #{key} must be an Array"
|
|
344
344
|
end
|
|
345
345
|
|
|
@@ -347,7 +347,7 @@ module Phronomy
|
|
|
347
347
|
value = hash.fetch(key)
|
|
348
348
|
return value if value.is_a?(Hash)
|
|
349
349
|
|
|
350
|
-
raise Phronomy::
|
|
350
|
+
raise Phronomy::Context::ManifestError,
|
|
351
351
|
"#{label} #{key} must be a Hash"
|
|
352
352
|
end
|
|
353
353
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
module
|
|
4
|
+
module Context
|
|
5
5
|
# Typed, canonicalizable input customization returned by before_llm_input.
|
|
6
6
|
# v1 deliberately exposes only fields that are fully applied to every call.
|
|
7
7
|
LLMInputPatch = Data.define(:model_config_patch, :segment_candidates) do
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Context
|
|
5
|
+
# A prompt template that substitutes {{variable}} placeholders in a string.
|
|
6
|
+
#
|
|
7
|
+
# @example Simple human template
|
|
8
|
+
# t = Phronomy::Context::PromptTemplate.new(template: "Translate to {{lang}}: {{text}}")
|
|
9
|
+
# t.format(lang: "French", text: "Hello")
|
|
10
|
+
# # => "Translate to French: Hello"
|
|
11
|
+
#
|
|
12
|
+
# @example With a system template
|
|
13
|
+
# t = Phronomy::Context::PromptTemplate.new(
|
|
14
|
+
# template: "{{question}}",
|
|
15
|
+
# system_template: "You are a {{role}} assistant."
|
|
16
|
+
# )
|
|
17
|
+
# t.format_system(role: "helpful")
|
|
18
|
+
# # => "You are a helpful assistant."
|
|
19
|
+
#
|
|
20
|
+
# As a Runnable, #invoke accepts a Hash of variables and returns a Hash
|
|
21
|
+
# with :prompt (and optionally :system) keys.
|
|
22
|
+
class PromptTemplate
|
|
23
|
+
include Phronomy::Runnable
|
|
24
|
+
|
|
25
|
+
PLACEHOLDER = /\{\{(\w+)\}\}/
|
|
26
|
+
|
|
27
|
+
attr_reader :template, :system_template
|
|
28
|
+
|
|
29
|
+
# @param template [String] human message template with {{var}} placeholders
|
|
30
|
+
# @param system_template [String, nil] optional system message template
|
|
31
|
+
# @api public
|
|
32
|
+
def initialize(template:, system_template: nil)
|
|
33
|
+
@template = template
|
|
34
|
+
@system_template = system_template
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Substitute all {{var}} placeholders in the human template.
|
|
38
|
+
#
|
|
39
|
+
# @param variables [Hash{Symbol => String}]
|
|
40
|
+
# @return [String]
|
|
41
|
+
# @api public
|
|
42
|
+
def format(**variables)
|
|
43
|
+
substitute(@template, variables)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Substitute all {{var}} placeholders in the system template.
|
|
47
|
+
# Returns nil when no system template was set.
|
|
48
|
+
#
|
|
49
|
+
# @param variables [Hash{Symbol => String}]
|
|
50
|
+
# @return [String, nil]
|
|
51
|
+
# @api public
|
|
52
|
+
def format_system(**variables)
|
|
53
|
+
@system_template && substitute(@system_template, variables)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Runnable interface: accepts a Hash of variable values.
|
|
57
|
+
# Returns { prompt: String, system: String|nil }.
|
|
58
|
+
#
|
|
59
|
+
# @param input [Hash{Symbol => String}]
|
|
60
|
+
# @return [Hash]
|
|
61
|
+
# @api public
|
|
62
|
+
def invoke(input, config: {})
|
|
63
|
+
vars = normalize_input(input)
|
|
64
|
+
result = {prompt: format(**vars)}
|
|
65
|
+
sys = format_system(**vars)
|
|
66
|
+
result[:system] = sys if sys
|
|
67
|
+
result
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns the list of placeholder names found in both templates.
|
|
71
|
+
#
|
|
72
|
+
# @return [Array<Symbol>]
|
|
73
|
+
# @api public
|
|
74
|
+
def variables
|
|
75
|
+
names = @template.scan(PLACEHOLDER).flatten
|
|
76
|
+
names += @system_template.scan(PLACEHOLDER).flatten if @system_template
|
|
77
|
+
names.map(&:to_sym).uniq
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def substitute(text, variables)
|
|
83
|
+
text.gsub(PLACEHOLDER) do |match|
|
|
84
|
+
key = Regexp.last_match(1).to_sym
|
|
85
|
+
variables.fetch(key) { raise KeyError, "Missing variable: {{#{key}}}" }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize_input(input)
|
|
90
|
+
case input
|
|
91
|
+
when Hash then input
|
|
92
|
+
when String then {input: input}
|
|
93
|
+
else raise ArgumentError, "PromptTemplate#invoke expects a Hash of variables, got #{input.class}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "csv"
|
|
4
4
|
|
|
5
5
|
module Phronomy
|
|
6
|
-
module
|
|
6
|
+
module Documents
|
|
7
7
|
module Loader
|
|
8
8
|
# Loads a CSV file, converting each row into a separate document.
|
|
9
9
|
#
|
|
@@ -12,7 +12,7 @@ module Phronomy
|
|
|
12
12
|
# a human-readable "key: value" string for embedding.
|
|
13
13
|
#
|
|
14
14
|
# @example
|
|
15
|
-
# loader = Phronomy::
|
|
15
|
+
# loader = Phronomy::Documents::Loader::CsvLoader.new
|
|
16
16
|
# docs = loader.load("products.csv")
|
|
17
17
|
# # => [
|
|
18
18
|
# # { text: "name: Widget\nprice: 9.99", metadata: { source: "...", row: 1, name: "Widget", price: "9.99" } },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
module
|
|
4
|
+
module Documents
|
|
5
5
|
module Loader
|
|
6
6
|
# Loads a Markdown file, optionally splitting on top-level headings.
|
|
7
7
|
#
|
|
@@ -10,12 +10,12 @@ module Phronomy
|
|
|
10
10
|
# rather than the full file at once.
|
|
11
11
|
#
|
|
12
12
|
# @example Single document (heading split disabled)
|
|
13
|
-
# loader = Phronomy::
|
|
13
|
+
# loader = Phronomy::Documents::Loader::MarkdownLoader.new(split_on_headings: false)
|
|
14
14
|
# docs = loader.load("README.md")
|
|
15
15
|
# # => [{ text: "# Title\n...", metadata: { source: "README.md" } }]
|
|
16
16
|
#
|
|
17
17
|
# @example Split per heading (default)
|
|
18
|
-
# loader = Phronomy::
|
|
18
|
+
# loader = Phronomy::Documents::Loader::MarkdownLoader.new
|
|
19
19
|
# docs = loader.load("guide.md")
|
|
20
20
|
# # => [
|
|
21
21
|
# # { text: "# Section 1\n...", metadata: { source: "guide.md", section: "Section 1" } },
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
module
|
|
4
|
+
module Documents
|
|
5
5
|
module Loader
|
|
6
6
|
# Loads a plain-text file as a single document.
|
|
7
7
|
#
|
|
8
8
|
# @example
|
|
9
|
-
# loader = Phronomy::
|
|
9
|
+
# loader = Phronomy::Documents::Loader::PlainTextLoader.new
|
|
10
10
|
# docs = loader.load("/path/to/file.txt")
|
|
11
11
|
# # => [{ text: "...", metadata: { source: "/path/to/file.txt" } }]
|
|
12
12
|
class PlainTextLoader < Base
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
module
|
|
4
|
+
module Documents
|
|
5
5
|
module Splitter
|
|
6
6
|
# Splits text into fixed-size character chunks with optional overlap.
|
|
7
7
|
#
|
|
8
8
|
# @example
|
|
9
|
-
# splitter = Phronomy::
|
|
9
|
+
# splitter = Phronomy::Documents::Splitter::FixedSizeSplitter.new(chunk_size: 200, chunk_overlap: 20)
|
|
10
10
|
# chunks = splitter.split({ text: long_text, metadata: { source: "doc.txt" } })
|
|
11
11
|
# # => [
|
|
12
12
|
# # { text: "...(200 chars)...", metadata: { source: "doc.txt", chunk: 0 } },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
module
|
|
4
|
+
module Documents
|
|
5
5
|
module Splitter
|
|
6
6
|
# Splits text recursively using a prioritised list of separator strings.
|
|
7
7
|
#
|
|
@@ -18,7 +18,7 @@ module Phronomy
|
|
|
18
18
|
# 5. "" — character-level fallback
|
|
19
19
|
#
|
|
20
20
|
# @example
|
|
21
|
-
# splitter = Phronomy::
|
|
21
|
+
# splitter = Phronomy::Documents::Splitter::RecursiveSplitter.new(chunk_size: 300, chunk_overlap: 30)
|
|
22
22
|
# chunks = splitter.split({ text: long_markdown, metadata: { source: "guide.md" } })
|
|
23
23
|
class RecursiveSplitter < Base
|
|
24
24
|
DEFAULT_SEPARATORS = ["\n\n", "\n", ". ", " ", ""].freeze
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Embeddings
|
|
5
|
+
# Framework-owned asynchronous execution of a synchronous embedding adapter.
|
|
6
|
+
#
|
|
7
|
+
# Construction starts no Runtime. Each operation uses the current default
|
|
8
|
+
# pool unless a caller-owned pool was injected. Timeout includes queue wait
|
|
9
|
+
# and settles the result without forcibly interrupting an executing adapter.
|
|
10
|
+
#
|
|
11
|
+
# @api public
|
|
12
|
+
class AsyncClient
|
|
13
|
+
# @param adapter [Embeddings::Base] synchronous embedding adapter
|
|
14
|
+
# @param pool [Concurrency::OffloadPool, nil] optional execution pool
|
|
15
|
+
# @api public
|
|
16
|
+
def initialize(adapter:, pool: nil)
|
|
17
|
+
@adapter = adapter
|
|
18
|
+
@pool = pool
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Submits a synchronous embed call without waiting for queue capacity.
|
|
22
|
+
#
|
|
23
|
+
# @param text [String]
|
|
24
|
+
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
|
|
25
|
+
# @param timeout [Numeric, nil] operation-wide submit timeout
|
|
26
|
+
# @return [Phronomy::TaskResult] the pool's original completion handle
|
|
27
|
+
# @api public
|
|
28
|
+
def embed_async(text, cancellation_token = nil, timeout: nil)
|
|
29
|
+
Phronomy::Execution.submit(pool: default_pool,
|
|
30
|
+
timeout: timeout,
|
|
31
|
+
cancellation_token: cancellation_token,
|
|
32
|
+
on_full: :raise) do
|
|
33
|
+
@adapter.embed(text, cancellation_token)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def default_pool
|
|
40
|
+
@pool
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Embeddings
|
|
5
|
+
# Embeddings adapter backed by RubyLLM.
|
|
6
|
+
#
|
|
7
|
+
# Delegates to +RubyLLM.embed+ and returns the resulting vector as an
|
|
8
|
+
# +Array<Float>+.
|
|
9
|
+
#
|
|
10
|
+
# @example Default model
|
|
11
|
+
# embeddings = Phronomy::Embeddings::RubyLLMEmbeddings.new
|
|
12
|
+
# vector = embeddings.embed("Hello, world!")
|
|
13
|
+
#
|
|
14
|
+
# @example Explicit model
|
|
15
|
+
# embeddings = Phronomy::Embeddings::RubyLLMEmbeddings.new(model: "text-embedding-3-small")
|
|
16
|
+
# vector = embeddings.embed("Hello, world!")
|
|
17
|
+
class RubyLLMEmbeddings < Base
|
|
18
|
+
# @param model [String, nil] embedding model identifier; nil uses the RubyLLM default
|
|
19
|
+
# @param provider [Symbol, nil] provider override (e.g. :openai); nil uses the RubyLLM default
|
|
20
|
+
# @param assume_model_exists [Boolean] when true, skips RubyLLM model-registry validation
|
|
21
|
+
# (useful for locally hosted models not in the registry)
|
|
22
|
+
# @api public
|
|
23
|
+
def initialize(model: nil, provider: nil, assume_model_exists: false)
|
|
24
|
+
@model = model
|
|
25
|
+
@provider = provider
|
|
26
|
+
@assume_model_exists = assume_model_exists
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
protected
|
|
30
|
+
|
|
31
|
+
# Embed text via RubyLLM.
|
|
32
|
+
#
|
|
33
|
+
# @param text [String]
|
|
34
|
+
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil] optional; raises CancellationError when cancelled
|
|
35
|
+
# @return [Array<Float>]
|
|
36
|
+
# @api public
|
|
37
|
+
def perform_embed(text, cancellation_token = nil)
|
|
38
|
+
opts = {}
|
|
39
|
+
opts[:model] = @model if @model
|
|
40
|
+
opts[:provider] = @provider if @provider
|
|
41
|
+
opts[:assume_model_exists] = true if @assume_model_exists
|
|
42
|
+
::RubyLLM.embed(text, **opts).vectors
|
|
43
|
+
rescue ::RubyLLM::Error => error
|
|
44
|
+
raise TransportError, error.message
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "invalid_result_error"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Embeddings
|
|
7
|
+
# Owns input validation, the cancellation checkpoint and result validation.
|
|
8
|
+
# Providers implement perform_embed; execution belongs to AsyncClient.
|
|
9
|
+
# This contract selects no provider or vector store.
|
|
10
|
+
# @api public
|
|
11
|
+
class Base
|
|
12
|
+
# @param text [String] empty text is allowed; model restrictions are provider-owned
|
|
13
|
+
# @param cancellation_token [#raise_if_cancelled!, nil]
|
|
14
|
+
# @return [Array<Float>] non-empty finite vector
|
|
15
|
+
# @raise [ArgumentError] when input is not text
|
|
16
|
+
# @raise [InvalidResultError] when the provider returns a malformed vector
|
|
17
|
+
# @api public
|
|
18
|
+
def embed(text, cancellation_token = nil)
|
|
19
|
+
cancellation_token&.raise_if_cancelled!
|
|
20
|
+
raise ArgumentError, "text must be a String" unless text.is_a?(String)
|
|
21
|
+
vector = perform_embed(text, cancellation_token)
|
|
22
|
+
unless vector.is_a?(Array) && !vector.empty? && vector.all? { |value|
|
|
23
|
+
value.is_a?(Numeric) && value.real? && value.finite? && value.to_f.finite?
|
|
24
|
+
}
|
|
25
|
+
raise InvalidResultError, "embed must return a non-empty Array of finite real numbers"
|
|
26
|
+
end
|
|
27
|
+
vector.map(&:to_f)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
protected
|
|
31
|
+
|
|
32
|
+
# Provider extension point. Preserve cancellation and application errors;
|
|
33
|
+
# SDK adapters translate their own provider errors to TransportError.
|
|
34
|
+
# @param text [String]
|
|
35
|
+
# @param cancellation_token [#raise_if_cancelled!, nil]
|
|
36
|
+
# @return [Array<Numeric>]
|
|
37
|
+
# @api public
|
|
38
|
+
def perform_embed(text, cancellation_token = nil)
|
|
39
|
+
raise NotImplementedError, "#{self.class}#perform_embed is not implemented"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../common/error"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Embeddings
|
|
7
|
+
# A provider returned data that violates the embedding result contract.
|
|
8
|
+
# @api public
|
|
9
|
+
class InvalidResultError < Phronomy::Error; end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../common/error"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Embeddings
|
|
7
|
+
# An embedding provider failed; the original SDK exception remains #cause.
|
|
8
|
+
# @api public
|
|
9
|
+
class TransportError < Phronomy::Error; end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "worker_input_restricted"
|
|
3
|
+
require_relative "../../execution/concurrency/worker_input_restricted"
|
|
4
4
|
|
|
5
5
|
module Phronomy
|
|
6
6
|
module Concurrency
|
|
@@ -81,14 +81,12 @@ module Phronomy
|
|
|
81
81
|
@submitted_at = submitted_at ||
|
|
82
82
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
83
83
|
@mutex = Mutex.new
|
|
84
|
-
@timer_subscriptions =
|
|
84
|
+
@timer_subscriptions = ResultSubscriptions.new
|
|
85
|
+
@cancellation_subscriptions = ResultSubscriptions.new
|
|
85
86
|
|
|
86
87
|
# Explicit submit cancellation is operation-wide. Deadline-only tokens are
|
|
87
88
|
# promoted to cancel! by OffloadPool#submit using the Runtime timer queue.
|
|
88
|
-
@
|
|
89
|
-
-> { fire_cancellation! }
|
|
90
|
-
end
|
|
91
|
-
@cancellation_token&.on_cancel(&@cancellation_callback)
|
|
89
|
+
@cancellation_subscriptions.explicit_cancellation(@cancellation_token) { fire_cancellation! }
|
|
92
90
|
end
|
|
93
91
|
|
|
94
92
|
# @return [Boolean] true when caller-facing settlement has been claimed
|
|
@@ -260,7 +258,7 @@ module Phronomy
|
|
|
260
258
|
def notify_abandoned
|
|
261
259
|
@on_abandoned&.call(self)
|
|
262
260
|
rescue => error
|
|
263
|
-
Phronomy.
|
|
261
|
+
Phronomy::RuntimeSettings.current.logger&.error do
|
|
264
262
|
"OffloadPool abandoned callback failed: #{error.class}: #{error.message}"
|
|
265
263
|
end
|
|
266
264
|
end
|
|
@@ -293,13 +291,7 @@ module Phronomy
|
|
|
293
291
|
|
|
294
292
|
def detach_submit_cancellation
|
|
295
293
|
@timer_subscriptions.close
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
@cancellation_token.send(
|
|
299
|
-
:unregister_cancel_callback,
|
|
300
|
-
@cancellation_callback
|
|
301
|
-
)
|
|
302
|
-
@cancellation_callback = nil
|
|
294
|
+
@cancellation_subscriptions.close
|
|
303
295
|
end
|
|
304
296
|
end
|
|
305
297
|
private_constant :Operation
|
|
@@ -384,7 +376,7 @@ module Phronomy
|
|
|
384
376
|
full_timeout: nil,
|
|
385
377
|
&block
|
|
386
378
|
)
|
|
387
|
-
if Phronomy::
|
|
379
|
+
if Phronomy::WaitPolicy.blocking_forbidden? && on_full != :raise
|
|
388
380
|
raise Phronomy::EventLoopReentrancyError,
|
|
389
381
|
"OffloadPool admission cannot wait on EventLoop; use on_full: :raise"
|
|
390
382
|
end
|
|
@@ -491,19 +483,42 @@ module Phronomy
|
|
|
491
483
|
task
|
|
492
484
|
end
|
|
493
485
|
|
|
494
|
-
#
|
|
495
|
-
#
|
|
486
|
+
# Close admission without waiting for worker termination. Already queued
|
|
487
|
+
# operations drain normally; running workers are never interrupted.
|
|
488
|
+
# @api private
|
|
489
|
+
def begin_shutdown
|
|
490
|
+
@shutdown = true
|
|
491
|
+
@queue.close
|
|
492
|
+
self
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# Whether shutdown has closed admission and all workers have exited.
|
|
496
|
+
# Logical TaskResult settlement alone does not establish this fact.
|
|
497
|
+
# @api private
|
|
498
|
+
def terminated?
|
|
499
|
+
@shutdown && @workers.none?(&:alive?)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Gracefully drains the pool and waits for worker threads to terminate.
|
|
503
|
+
# All workers share +drain_timeout+ or the supplied monotonic +deadline+.
|
|
504
|
+
# A deadline expiry leaves workers running; callers inspect terminated?.
|
|
496
505
|
#
|
|
497
506
|
# Closing the underlying SizedQueue signals workers to exit after draining
|
|
498
507
|
# remaining items, without blocking on a full-queue push.
|
|
499
508
|
#
|
|
500
509
|
# @param drain_timeout [Numeric] seconds to wait for workers to finish
|
|
510
|
+
# @param deadline [Numeric, nil] absolute monotonic shutdown deadline
|
|
501
511
|
# @return [self]
|
|
502
512
|
# @api private
|
|
503
|
-
def shutdown(drain_timeout: 30)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
@workers.each
|
|
513
|
+
def shutdown(drain_timeout: 30, deadline: nil)
|
|
514
|
+
deadline ||= Process.clock_gettime(Process::CLOCK_MONOTONIC) + drain_timeout
|
|
515
|
+
begin_shutdown
|
|
516
|
+
@workers.each do |thread|
|
|
517
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
518
|
+
break if remaining <= 0
|
|
519
|
+
|
|
520
|
+
thread.join(remaining)
|
|
521
|
+
end
|
|
507
522
|
self
|
|
508
523
|
end
|
|
509
524
|
|