pyfect-dev 0.1.0__py3-none-any.whl
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.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""The check of a provider's answers against their decisions (Effect's
|
|
2
|
+
``validateAnswer`` / ``settleAnswers`` in ``DecisionModel``): each answer
|
|
3
|
+
becomes an ``Ok`` answer or an ``Err(Unanswered)``, the first failed check
|
|
4
|
+
naming the reason."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
from collections.abc import Mapping, Sequence
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, ClassVar
|
|
12
|
+
|
|
13
|
+
from pyfect.ai import decision as D
|
|
14
|
+
from pyfect.result import Err, Ok
|
|
15
|
+
|
|
16
|
+
_SUM_TOLERANCE = 1e-6
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class ProviderClassifyAnswer:
|
|
21
|
+
"""A classify answer; ``probabilities`` ``None`` when the provider has
|
|
22
|
+
no distribution."""
|
|
23
|
+
|
|
24
|
+
tag: ClassVar[str] = "Classify"
|
|
25
|
+
label: str
|
|
26
|
+
probabilities: Mapping[str, float] | None = None
|
|
27
|
+
confidence: float | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class ProviderRateAnswer:
|
|
32
|
+
"""A rate answer: the label is the most probable level's, so the
|
|
33
|
+
provider gives none."""
|
|
34
|
+
|
|
35
|
+
tag: ClassVar[str] = "Rate"
|
|
36
|
+
rating: float
|
|
37
|
+
probabilities: Mapping[str, float] | None = None
|
|
38
|
+
confidence: float | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class ProviderProbabilityAnswer:
|
|
43
|
+
tag: ClassVar[str] = "Probability"
|
|
44
|
+
probability: float
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class ProviderRefusalAnswer:
|
|
49
|
+
"""The provider declined to answer."""
|
|
50
|
+
|
|
51
|
+
tag: ClassVar[str] = "Refusal"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
type ProviderAnswer = (
|
|
55
|
+
ProviderClassifyAnswer | ProviderRateAnswer | ProviderProbabilityAnswer | ProviderRefusalAnswer
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _unanswered(reason: D.UnansweredReason, description: str) -> Err[D.Unanswered]:
|
|
60
|
+
return Err(D.Unanswered(reason, description))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _invalid(description: str) -> Err[D.Unanswered]:
|
|
64
|
+
return _unanswered("Invalid", description)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _is_unit(value: float) -> bool:
|
|
68
|
+
return math.isfinite(value) and 0 <= value <= 1
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _distribution(
|
|
72
|
+
key: str,
|
|
73
|
+
labels: Sequence[str],
|
|
74
|
+
raw: Mapping[str, float] | None,
|
|
75
|
+
rounding_error: float,
|
|
76
|
+
) -> D.Outcome[dict[str, float]]:
|
|
77
|
+
"""The distribution over ``labels``, rescaled to sum to 1 when its drift
|
|
78
|
+
is within the provider's rounding (``len(labels) * rounding_error``)."""
|
|
79
|
+
if raw is None:
|
|
80
|
+
return _unanswered(
|
|
81
|
+
"NoDistribution", f'Provider returned no probabilities for decision "{key}"'
|
|
82
|
+
)
|
|
83
|
+
probabilities: dict[str, float] = {}
|
|
84
|
+
total = 0.0 # added in order, as JavaScript does: builtin sum() is compensated
|
|
85
|
+
for label in labels:
|
|
86
|
+
value = raw.get(label)
|
|
87
|
+
if value is None or not _is_unit(value):
|
|
88
|
+
return _invalid(
|
|
89
|
+
f'Provider returned no probability for label "{label}" of decision "{key}"'
|
|
90
|
+
)
|
|
91
|
+
probabilities[label] = value
|
|
92
|
+
total += value
|
|
93
|
+
drift = abs(total - 1)
|
|
94
|
+
if total == 0 or drift > len(labels) * rounding_error + _SUM_TOLERANCE:
|
|
95
|
+
return _invalid(
|
|
96
|
+
f'Provider returned probabilities that do not sum to 1 for decision "{key}"'
|
|
97
|
+
)
|
|
98
|
+
if drift > _SUM_TOLERANCE:
|
|
99
|
+
probabilities = {label: value / total for label, value in probabilities.items()}
|
|
100
|
+
return Ok(probabilities)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _confidence(key: str, confidence: float | None) -> D.Outcome[float | None]:
|
|
104
|
+
if confidence is None or _is_unit(confidence):
|
|
105
|
+
return Ok(confidence)
|
|
106
|
+
return _invalid(f'Provider returned confidence outside [0, 1] for decision "{key}"')
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _classify(
|
|
110
|
+
key: str, decision: D.Classify[Any], answer: ProviderClassifyAnswer, rounding_error: float
|
|
111
|
+
) -> D.Outcome[D.AnyAnswer]:
|
|
112
|
+
labels = list(decision.criteria)
|
|
113
|
+
if answer.label not in labels:
|
|
114
|
+
return _invalid(f'Provider returned an unknown label for decision "{key}"')
|
|
115
|
+
confidence = _confidence(key, answer.confidence)
|
|
116
|
+
if isinstance(confidence, Err):
|
|
117
|
+
return confidence
|
|
118
|
+
probabilities = _distribution(key, labels, answer.probabilities, rounding_error)
|
|
119
|
+
if isinstance(probabilities, Err):
|
|
120
|
+
return probabilities
|
|
121
|
+
return Ok(D.ClassifyAnswer(answer.label, probabilities.value, confidence.value))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _rate(
|
|
125
|
+
key: str, decision: D.Rate[Any], answer: ProviderRateAnswer, rounding_error: float
|
|
126
|
+
) -> D.Outcome[D.AnyAnswer]:
|
|
127
|
+
levels = [level.label for level in decision.criteria]
|
|
128
|
+
rating = answer.rating
|
|
129
|
+
if not (math.isfinite(rating) and 0 <= rating <= len(levels) - 1):
|
|
130
|
+
return _invalid(
|
|
131
|
+
f'Provider returned a rating outside [0, {len(levels) - 1}] for decision "{key}"'
|
|
132
|
+
)
|
|
133
|
+
confidence = _confidence(key, answer.confidence)
|
|
134
|
+
if isinstance(confidence, Err):
|
|
135
|
+
return confidence
|
|
136
|
+
probabilities = _distribution(key, levels, answer.probabilities, rounding_error)
|
|
137
|
+
if isinstance(probabilities, Err):
|
|
138
|
+
return probabilities
|
|
139
|
+
found = probabilities.value
|
|
140
|
+
label = levels[0]
|
|
141
|
+
for level in levels[1:]:
|
|
142
|
+
if found[level] > found[label]:
|
|
143
|
+
label = level
|
|
144
|
+
return Ok(D.RateAnswer(rating, label, found, confidence.value))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _probability(key: str, answer: ProviderProbabilityAnswer) -> D.Outcome[D.ProbabilityAnswer]:
|
|
148
|
+
if not _is_unit(answer.probability):
|
|
149
|
+
return _invalid(f'Provider returned a probability outside [0, 1] for decision "{key}"')
|
|
150
|
+
return Ok(D.ProbabilityAnswer(answer.probability))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def validate(
|
|
154
|
+
key: str, decision: D.AnyDecision, answer: ProviderAnswer | None, rounding_error: float
|
|
155
|
+
) -> D.Outcome[D.AnyAnswer]:
|
|
156
|
+
"""``answer`` checked against ``decision`` (named ``key``)."""
|
|
157
|
+
if answer is None:
|
|
158
|
+
return _unanswered("Missing", f'Provider returned no answer for decision "{key}"')
|
|
159
|
+
match decision, answer:
|
|
160
|
+
case _, ProviderRefusalAnswer():
|
|
161
|
+
return _unanswered("Refused", f'Provider refused to answer decision "{key}"')
|
|
162
|
+
case D.Classify(), ProviderClassifyAnswer():
|
|
163
|
+
return _classify(key, decision, answer, rounding_error)
|
|
164
|
+
case D.Rate(), ProviderRateAnswer():
|
|
165
|
+
return _rate(key, decision, answer, rounding_error)
|
|
166
|
+
case D.Probability(), ProviderProbabilityAnswer():
|
|
167
|
+
return _probability(key, answer)
|
|
168
|
+
case _:
|
|
169
|
+
return _invalid(
|
|
170
|
+
f'Provider returned a "{answer.tag}" answer for decision "{key}", '
|
|
171
|
+
f'expected "{decision.tag}"'
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def settle(
|
|
176
|
+
decisions: Mapping[str, D.AnyDecision],
|
|
177
|
+
answers: Mapping[str, ProviderAnswer],
|
|
178
|
+
rounding_error: float,
|
|
179
|
+
) -> dict[str, D.Outcome[D.AnyAnswer]]:
|
|
180
|
+
"""Every decision's outcome, in definition order; answers to other
|
|
181
|
+
names are dropped."""
|
|
182
|
+
return {
|
|
183
|
+
key: validate(key, decision, answers.get(key), rounding_error)
|
|
184
|
+
for key, decision in decisions.items()
|
|
185
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""What a generated provider client is built with (the ``make`` of Effect's
|
|
2
|
+
generated ``Generated.ts`` clients): each operation is an ``Operation``
|
|
3
|
+
(its method, query and header parameters, and the schema of each status),
|
|
4
|
+
and a ``GeneratedClient`` sends it.
|
|
5
|
+
|
|
6
|
+
A response is decoded by the schema of its exact status, else of its
|
|
7
|
+
range (``"2xx"``); a documented error status fails with the client's
|
|
8
|
+
``ClientError`` subclass carrying the decoded body; any other status fails
|
|
9
|
+
with a ``StatusCodeError`` whose description is the body's JSON (or
|
|
10
|
+
``"Unexpected status code"``). Server-Sent Events and binary operations
|
|
11
|
+
check the status only; they skip the ``transform_client`` hook unless the
|
|
12
|
+
client sets ``transform_streams``, as Effect's OpenRouter and OpenAI clients
|
|
13
|
+
differ.
|
|
14
|
+
|
|
15
|
+
An ``encoded_path`` is Effect's ``__makePathRequest``: its parameters are
|
|
16
|
+
percent-encoded as ``encodeURIComponent`` does, and an empty parameter, a dot
|
|
17
|
+
segment or a parameter that cannot be encoded fails the operation with an
|
|
18
|
+
``InvalidUrlError``, without sending."""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
from collections.abc import Callable, Mapping
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from typing import Any, ClassVar, Literal, cast
|
|
26
|
+
from urllib.parse import quote
|
|
27
|
+
|
|
28
|
+
import pyfect as fx
|
|
29
|
+
from pyfect import _json_text
|
|
30
|
+
from pyfect import schema as S
|
|
31
|
+
from pyfect import stream as St
|
|
32
|
+
from pyfect._core import Effect
|
|
33
|
+
from pyfect.encoding import sse
|
|
34
|
+
from pyfect.errors import TaggedError
|
|
35
|
+
from pyfect.http import request as http_request
|
|
36
|
+
from pyfect.http.body import FormDataInput
|
|
37
|
+
from pyfect.http.client import HttpClientService
|
|
38
|
+
from pyfect.http.error import HttpClientError, InvalidUrlError, StatusCodeError
|
|
39
|
+
from pyfect.http.request import HttpClientRequest
|
|
40
|
+
from pyfect.http.response import HttpClientResponse, StatusRange, match_status
|
|
41
|
+
from pyfect.stream import Stream
|
|
42
|
+
|
|
43
|
+
type Method = Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
|
|
44
|
+
type ClientTransform = Callable[
|
|
45
|
+
[HttpClientService[HttpClientError]], Effect[HttpClientService[HttpClientError]]
|
|
46
|
+
]
|
|
47
|
+
"""Effect's ``transformClient`` option: the client an operation runs on,
|
|
48
|
+
chosen per call."""
|
|
49
|
+
|
|
50
|
+
type Params = Mapping[str, Any]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(eq=False)
|
|
54
|
+
class ClientError(TaggedError):
|
|
55
|
+
"""A documented error status of an operation: ``tag`` names the
|
|
56
|
+
operation and the status (``"SendChatCompletionRequest400"``), ``cause``
|
|
57
|
+
is the decoded body."""
|
|
58
|
+
|
|
59
|
+
tag: str
|
|
60
|
+
cause: Any
|
|
61
|
+
request: HttpClientRequest
|
|
62
|
+
response: HttpClientResponse
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class JsonBody:
|
|
67
|
+
payload: object
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True)
|
|
71
|
+
class FormBody:
|
|
72
|
+
payload: FormDataInput
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
type Body = JsonBody | FormBody | None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class _Decoder:
|
|
80
|
+
"""A schema's decoding of JSON, built on first use (the generated
|
|
81
|
+
schemas are many; most are never decoded)."""
|
|
82
|
+
|
|
83
|
+
schema: Any
|
|
84
|
+
_decode: Callable[[object], Effect[Any, S.SchemaError]] | None = field(default=None)
|
|
85
|
+
|
|
86
|
+
def __call__(self, json: object) -> Effect[Any, S.SchemaError]:
|
|
87
|
+
if self._decode is None:
|
|
88
|
+
self._decode = S.decode_unknown_effect(S.to_codec_json(self.schema))
|
|
89
|
+
return self._decode(_json_text.whole_numbers_as_ints(json)) # pyright: ignore[reportArgumentType]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True)
|
|
93
|
+
class Operation:
|
|
94
|
+
"""An operation: ``success`` decodes a 2xx body, ``status_success``
|
|
95
|
+
the body of an exact status, ``binary_success`` gives a 2xx body as
|
|
96
|
+
bytes, a ``void`` status succeeds with ``None`` (a 2xx none of them
|
|
97
|
+
covers is an unexpected status), ``errors`` maps a status to its tag and
|
|
98
|
+
body schema."""
|
|
99
|
+
|
|
100
|
+
method: Method
|
|
101
|
+
url_params: tuple[str, ...] = ()
|
|
102
|
+
headers: tuple[str, ...] = ()
|
|
103
|
+
success: Any = None
|
|
104
|
+
status_success: Mapping[int, Any] = field(default_factory=dict[int, Any])
|
|
105
|
+
binary_success: bool = False
|
|
106
|
+
void: tuple[int, ...] = ()
|
|
107
|
+
errors: Mapping[int, tuple[str, Any]] = field(default_factory=dict[int, tuple[str, Any]])
|
|
108
|
+
_success: _Decoder | None = field(default=None, init=False, compare=False)
|
|
109
|
+
_errors: dict[int, _Decoder] = field(default_factory=dict[int, _Decoder], init=False)
|
|
110
|
+
_status_success: dict[int, _Decoder] = field(default_factory=dict[int, _Decoder], init=False)
|
|
111
|
+
|
|
112
|
+
def __post_init__(self) -> None:
|
|
113
|
+
if self.success is not None:
|
|
114
|
+
object.__setattr__(self, "_success", _Decoder(self.success))
|
|
115
|
+
self._status_success.update(
|
|
116
|
+
{status: _Decoder(schema) for status, schema in self.status_success.items()}
|
|
117
|
+
)
|
|
118
|
+
self._errors.update(
|
|
119
|
+
{status: _Decoder(schema) for status, (_, schema) in self.errors.items()}
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass(frozen=True)
|
|
124
|
+
class EncodedPath:
|
|
125
|
+
"""A path template of ``{}`` and the parameters not yet encoded into it."""
|
|
126
|
+
|
|
127
|
+
template: str
|
|
128
|
+
parameters: tuple[str, ...]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
type Path = str | EncodedPath
|
|
132
|
+
|
|
133
|
+
_DOT_SEGMENT = re.compile(r"(?:\.|%2e){1,2}", re.IGNORECASE)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def encoded_path(template: str, *parameters: str) -> EncodedPath:
|
|
137
|
+
return EncodedPath(template, parameters)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _resolved_path(path: EncodedPath, operation: Operation) -> Effect[str, HttpClientError]:
|
|
141
|
+
def fail(description: str, cause: object = None) -> Effect[str, HttpClientError]:
|
|
142
|
+
request = http_request.make(operation.method, "")
|
|
143
|
+
return fx.fail(HttpClientError(InvalidUrlError(request, cause, description)))
|
|
144
|
+
|
|
145
|
+
if any(value == "" or _DOT_SEGMENT.fullmatch(value) for value in path.parameters):
|
|
146
|
+
return fail("Path parameters must be non-empty and cannot be dot segments")
|
|
147
|
+
try:
|
|
148
|
+
encoded = [quote(value, safe="!'()*") for value in path.parameters]
|
|
149
|
+
except UnicodeEncodeError as cause:
|
|
150
|
+
return fail("Failed to encode path parameter", cause)
|
|
151
|
+
resolved = path.template.format(*encoded)
|
|
152
|
+
if any(_DOT_SEGMENT.fullmatch(segment) for segment in resolved.split("/")):
|
|
153
|
+
return fail("Request paths cannot contain dot segments")
|
|
154
|
+
return fx.succeed(resolved)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def json_body(payload: object) -> JsonBody:
|
|
158
|
+
return JsonBody(payload)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def form_body(payload: Mapping[str, Any]) -> FormBody:
|
|
162
|
+
"""A form body of a payload's fields (a generated ``TypedDict``)."""
|
|
163
|
+
return FormBody(cast("FormDataInput", payload))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _unexpected_status(response: HttpClientResponse) -> Effect[Any, HttpClientError]:
|
|
167
|
+
def fail(description: str) -> Effect[Any, HttpClientError]:
|
|
168
|
+
reason = StatusCodeError(response.request, response, None, description)
|
|
169
|
+
return fx.fail(HttpClientError(reason))
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
response.json.map(
|
|
173
|
+
lambda json: json if isinstance(json, str) else _json_text.stringify(json)
|
|
174
|
+
)
|
|
175
|
+
.or_else_succeed(lambda _: "Unexpected status code")
|
|
176
|
+
.flat_map(fail)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class GeneratedClient:
|
|
181
|
+
"""The base of a generated client: ``http_client`` sends every
|
|
182
|
+
operation, through ``transform_client`` for the JSON ones (and the
|
|
183
|
+
streams, with ``transform_streams``)."""
|
|
184
|
+
|
|
185
|
+
__slots__ = ("_transform", "http_client")
|
|
186
|
+
|
|
187
|
+
error_class: ClassVar[type[ClientError]] = ClientError
|
|
188
|
+
transform_streams: ClassVar[bool] = False
|
|
189
|
+
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
http_client: HttpClientService[HttpClientError],
|
|
193
|
+
transform_client: ClientTransform | None = None,
|
|
194
|
+
) -> None:
|
|
195
|
+
self.http_client = http_client
|
|
196
|
+
self._transform = transform_client
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def _request(
|
|
200
|
+
operation: Operation, path: str, params: Params | None, body: Body
|
|
201
|
+
) -> HttpClientRequest:
|
|
202
|
+
request = http_request.make(operation.method, path)
|
|
203
|
+
if params is not None:
|
|
204
|
+
query = [(key, params[key]) for key in operation.url_params if key in params]
|
|
205
|
+
if query:
|
|
206
|
+
request = request.set_url_params(query)
|
|
207
|
+
headers = {key: params.get(key) for key in operation.headers}
|
|
208
|
+
request = request.set_headers(headers)
|
|
209
|
+
match body:
|
|
210
|
+
case JsonBody(payload):
|
|
211
|
+
return request.body_json_unsafe(payload)
|
|
212
|
+
case FormBody(payload):
|
|
213
|
+
return request.body_form_data_record(payload)
|
|
214
|
+
case None:
|
|
215
|
+
return request
|
|
216
|
+
|
|
217
|
+
def _prepared(
|
|
218
|
+
self, operation: Operation, path: Path, params: Params | None, body: Body
|
|
219
|
+
) -> Effect[HttpClientRequest, HttpClientError]:
|
|
220
|
+
if isinstance(path, str):
|
|
221
|
+
return fx.succeed(self._request(operation, path, params, body))
|
|
222
|
+
return _resolved_path(path, operation).map(
|
|
223
|
+
lambda resolved: self._request(operation, resolved, params, body)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def _json(
|
|
227
|
+
self,
|
|
228
|
+
operation: Operation,
|
|
229
|
+
path: Path,
|
|
230
|
+
params: Params | None,
|
|
231
|
+
body: Body,
|
|
232
|
+
include_response: bool,
|
|
233
|
+
) -> Effect[Any, Any]:
|
|
234
|
+
error_class = self.error_class
|
|
235
|
+
|
|
236
|
+
def decode_error(status: int) -> Callable[[HttpClientResponse], Effect[Any, Any]]:
|
|
237
|
+
tag = operation.errors[status][0]
|
|
238
|
+
decode = operation._errors[status] # pyright: ignore[reportPrivateUsage]
|
|
239
|
+
|
|
240
|
+
def run(response: HttpClientResponse) -> Effect[Any, Any]:
|
|
241
|
+
return response.json.flat_map(decode).flat_map(
|
|
242
|
+
lambda cause: fx.fail(error_class(tag, cause, response.request, response))
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
return run
|
|
246
|
+
|
|
247
|
+
cases: dict[int | StatusRange, Callable[[HttpClientResponse], Effect[Any, Any]]] = {
|
|
248
|
+
status: decode_error(status) for status in operation.errors
|
|
249
|
+
}
|
|
250
|
+
for status in operation.void:
|
|
251
|
+
cases[status] = lambda _: fx.succeed(None)
|
|
252
|
+
success = operation._success # pyright: ignore[reportPrivateUsage]
|
|
253
|
+
if success is not None:
|
|
254
|
+
cases["2xx"] = lambda response: response.json.flat_map(success)
|
|
255
|
+
for status, decode in operation._status_success.items(): # pyright: ignore[reportPrivateUsage]
|
|
256
|
+
cases[status] = lambda response, decode=decode: response.json.flat_map(decode)
|
|
257
|
+
if operation.binary_success:
|
|
258
|
+
cases["2xx"] = lambda response: response.bytes_
|
|
259
|
+
|
|
260
|
+
def respond(response: HttpClientResponse) -> Effect[Any, Any]:
|
|
261
|
+
decoded = match_status(response, cases, _unexpected_status)
|
|
262
|
+
return decoded.map(lambda value: (value, response)) if include_response else decoded
|
|
263
|
+
|
|
264
|
+
def send(request: HttpClientRequest) -> Effect[Any, Any]:
|
|
265
|
+
if self._transform is None:
|
|
266
|
+
return self.http_client.execute(request).flat_map(respond)
|
|
267
|
+
return self._transform(self.http_client).flat_map(
|
|
268
|
+
lambda client: client.execute(request).flat_map(respond)
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
return self._prepared(operation, path, params, body).flat_map(send)
|
|
272
|
+
|
|
273
|
+
def _stream_bytes(
|
|
274
|
+
self, operation: Operation, path: Path, params: Params | None, body: Body
|
|
275
|
+
) -> Stream[bytes, HttpClientError]:
|
|
276
|
+
def send(request: HttpClientRequest) -> Effect[HttpClientResponse, HttpClientError]:
|
|
277
|
+
if self._transform is None or not self.transform_streams:
|
|
278
|
+
return self.http_client.filter_status_ok().execute(request)
|
|
279
|
+
return self._transform(self.http_client).flat_map(
|
|
280
|
+
lambda client: client.filter_status_ok().execute(request)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
sent = self._prepared(operation, path, params, body).flat_map(send)
|
|
284
|
+
return St.unwrap(sent.map(lambda response: response.stream))
|
|
285
|
+
|
|
286
|
+
def _sse(
|
|
287
|
+
self, operation: Operation, path: Path, params: Params | None, body: Body, schema: Any
|
|
288
|
+
) -> Stream[sse.DataEvent[Any], Any]:
|
|
289
|
+
return (
|
|
290
|
+
self._stream_bytes(operation, path, params, body)
|
|
291
|
+
.decode_text()
|
|
292
|
+
.pipe_through_channel(sse.decode_data_schema(schema))
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
def _binary(
|
|
296
|
+
self, operation: Operation, path: Path, params: Params | None, body: Body
|
|
297
|
+
) -> Stream[bytes, HttpClientError]:
|
|
298
|
+
return self._stream_bytes(operation, path, params, body)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""What the generated provider schemas (``pyfect.ai.openai.generated``,
|
|
2
|
+
``pyfect.ai.openrouter.generated``) are built with. Each builder takes and
|
|
3
|
+
gives ``Any``: a generated schema's type is declared from
|
|
4
|
+
``generated_types`` (both come from the same source by the same
|
|
5
|
+
generator), so the type checker reads thousands of declarations instead
|
|
6
|
+
of solving each schema expression, and ``struct`` builds a struct from
|
|
7
|
+
its field schemas without reading the TypedDict's annotations (as
|
|
8
|
+
``S.Struct`` does, which would slow the import down)."""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import importlib
|
|
13
|
+
import re
|
|
14
|
+
from typing import Any, cast
|
|
15
|
+
|
|
16
|
+
from pyfect import schema as S
|
|
17
|
+
from pyfect.schema import ast
|
|
18
|
+
from pyfect.schema._struct import StructSchema
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def struct(_typed_dict: object, fields: dict[str, Any], optional: tuple[str, ...] = ()) -> Any:
|
|
22
|
+
"""The struct of these field schemas; the keys in ``optional`` may be
|
|
23
|
+
missing. The TypedDict names the struct's type for the reader."""
|
|
24
|
+
schemas = {name: schema.__codec__() for name, schema in fields.items()}
|
|
25
|
+
node = ast.Objects(
|
|
26
|
+
tuple(
|
|
27
|
+
ast.PropertySignature(name, schema.ast, optional=name in optional)
|
|
28
|
+
for name, schema in schemas.items()
|
|
29
|
+
),
|
|
30
|
+
(),
|
|
31
|
+
)
|
|
32
|
+
return cast("Any", StructSchema(node, schemas))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def union(*members: Any, mode: ast.UnionMode = "any_of") -> Any:
|
|
36
|
+
return S.Union(*members, mode=mode)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def array(item: Any) -> Any:
|
|
40
|
+
return S.Array(item)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def record(key: Any, value: Any) -> Any:
|
|
44
|
+
return S.Record(key, value)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def struct_with_rest(schema: Any, *records: Any) -> Any:
|
|
48
|
+
return S.StructWithRest(schema, *records)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def suspend(thunk: Any) -> Any:
|
|
52
|
+
return cast("Any", S.suspend(thunk))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def check(schema: Any, *checks: Any) -> Any:
|
|
56
|
+
return schema.check(*checks)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def literals(values: list[Any]) -> Any:
|
|
60
|
+
return S.Literals(values)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def literal(value: Any) -> Any:
|
|
64
|
+
return S.Literal(value)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def js_pattern(python: str, source: str) -> Any:
|
|
68
|
+
"""``is_pattern`` of the generator's Python translation of the
|
|
69
|
+
JavaScript pattern ``source``, which messages show."""
|
|
70
|
+
return S.is_pattern(re.compile(python), expected=f"a string matching the RegExp {source}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def instance_of(cls: type, **annotations: object) -> Any:
|
|
74
|
+
return cast("Any", S.instance_of(cls, **annotations))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def lazy(module: str, name: str) -> Any:
|
|
78
|
+
"""The schema ``name`` of ``module``, imported on first use: a
|
|
79
|
+
recursive schema's struct, declared after the schemas that use it."""
|
|
80
|
+
|
|
81
|
+
def load() -> Any:
|
|
82
|
+
return getattr(importlib.import_module(module), name)
|
|
83
|
+
|
|
84
|
+
return cast("Any", S.suspend(load))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MCP server and client internals."""
|
pyfect/ai/_mcp/batch.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""A JSON-RPC batch (2025-03-26): its messages handled together, its
|
|
2
|
+
responses gathered into one array."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from typing import Never, cast
|
|
8
|
+
|
|
9
|
+
from pyfect._core import Effect
|
|
10
|
+
from pyfect._fiber import Fiber
|
|
11
|
+
from pyfect.ai._mcp.dispatch import Connection, Send, classify
|
|
12
|
+
from pyfect.ai._mcp.http_admission import has_request_protocol_version
|
|
13
|
+
from pyfect.ai.mcp_schema import JsonObject
|
|
14
|
+
from pyfect.effect import gen, sync
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _method(message: object) -> object:
|
|
18
|
+
return cast("dict[str, object]", message).get("method") if isinstance(message, dict) else None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def accepts(connection: Connection, messages: Sequence[object]) -> bool:
|
|
22
|
+
"""Whether ``connection`` 's revision takes this batch: a non-empty one
|
|
23
|
+
with no ``initialize`` and no stateless request in it."""
|
|
24
|
+
protocol = connection.protocol
|
|
25
|
+
return (
|
|
26
|
+
protocol is not None
|
|
27
|
+
and protocol.accepts_batches
|
|
28
|
+
and len(messages) > 0
|
|
29
|
+
and not any(has_request_protocol_version(m) for m in messages)
|
|
30
|
+
and not any(_method(m) == "initialize" for m in messages)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def answer(
|
|
35
|
+
connection: Connection, messages: Sequence[object], notify: Send
|
|
36
|
+
) -> Effect[list[JsonObject]]:
|
|
37
|
+
"""The responses to the batch's requests and invalid messages, in the
|
|
38
|
+
order they finish; what the server sends meanwhile goes to ``notify``.
|
|
39
|
+
Interrupting it interrupts the requests."""
|
|
40
|
+
replies: list[JsonObject] = []
|
|
41
|
+
started: list[Fiber[None, Never]] = []
|
|
42
|
+
|
|
43
|
+
def out(message: JsonObject) -> Effect[None]:
|
|
44
|
+
if "method" in message:
|
|
45
|
+
return notify(message)
|
|
46
|
+
return sync(lambda: replies.append(message))
|
|
47
|
+
|
|
48
|
+
@gen
|
|
49
|
+
def run():
|
|
50
|
+
# Effect's JSON-RPC decoder drops batch items that are not objects.
|
|
51
|
+
objects: list[object] = [m for m in messages if isinstance(m, dict)]
|
|
52
|
+
for message in objects:
|
|
53
|
+
request = classify(message).request
|
|
54
|
+
if request is None:
|
|
55
|
+
yield from connection.dispatch(message, out)
|
|
56
|
+
else:
|
|
57
|
+
# Started like a lone request, so it can be cancelled.
|
|
58
|
+
started.append((yield from connection.start(request, out)))
|
|
59
|
+
for fiber in started:
|
|
60
|
+
yield from fiber.await_exit()
|
|
61
|
+
return replies
|
|
62
|
+
|
|
63
|
+
@gen
|
|
64
|
+
def interrupt_all():
|
|
65
|
+
for fiber in started:
|
|
66
|
+
yield from fiber.interrupt()
|
|
67
|
+
|
|
68
|
+
return run.on_interrupt(interrupt_all)
|