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
pyfect/ai/_provider.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""The HTTP plumbing the AI providers share (each Effect provider package's
|
|
2
|
+
``internal/errors.ts``): request and response details for errors and
|
|
3
|
+
parts, the mapping of an ``HttpClientError`` to an ``AiError``, numbers
|
|
4
|
+
and ``Retry-After`` read from headers, and the SSE events of a response.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import math
|
|
10
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
11
|
+
from dataclasses import dataclass, replace
|
|
12
|
+
from datetime import timedelta
|
|
13
|
+
from typing import Any, Never, cast
|
|
14
|
+
|
|
15
|
+
import pyfect as fx
|
|
16
|
+
from pyfect import _json_text, byte_size
|
|
17
|
+
from pyfect import schema as S
|
|
18
|
+
from pyfect._core import Effect
|
|
19
|
+
from pyfect._js import or_default
|
|
20
|
+
from pyfect._json_text import parse as parse_json_text
|
|
21
|
+
from pyfect.ai.ai_error import (
|
|
22
|
+
AiError,
|
|
23
|
+
HttpContext,
|
|
24
|
+
HttpRequestDetails,
|
|
25
|
+
HttpResponseDetails,
|
|
26
|
+
InvalidOutputError,
|
|
27
|
+
InvalidResponseError,
|
|
28
|
+
NetworkError,
|
|
29
|
+
)
|
|
30
|
+
from pyfect.byte_size import ByteSize, mebibytes
|
|
31
|
+
from pyfect.context import Tag
|
|
32
|
+
from pyfect.encoding import sse
|
|
33
|
+
from pyfect.http import headers as H
|
|
34
|
+
from pyfect.http.client import HttpClientService, transform_response
|
|
35
|
+
from pyfect.http.error import (
|
|
36
|
+
DecodeError,
|
|
37
|
+
EmptyBodyError,
|
|
38
|
+
EncodeError,
|
|
39
|
+
HttpClientError,
|
|
40
|
+
InvalidUrlError,
|
|
41
|
+
StatusCodeError,
|
|
42
|
+
TransportError,
|
|
43
|
+
)
|
|
44
|
+
from pyfect.http.headers import CurrentRedactedNames, current_redacted_names
|
|
45
|
+
from pyfect.http.request import HttpClientRequest
|
|
46
|
+
from pyfect.http.response import HttpClientResponse, with_body_limit
|
|
47
|
+
from pyfect.result import Ok
|
|
48
|
+
from pyfect.stream import Stream
|
|
49
|
+
|
|
50
|
+
type JsonObject = dict[str, Any]
|
|
51
|
+
"""A JSON object read from or sent to a provider."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def obj(value: object) -> JsonObject:
|
|
55
|
+
"""``value`` when it is a JSON object, else an empty one."""
|
|
56
|
+
return cast("JsonObject", value) if isinstance(value, dict) else {}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def arr(value: object) -> list[Any]:
|
|
60
|
+
"""``value`` when it is a JSON array, else an empty one."""
|
|
61
|
+
return cast("list[Any]", value) if isinstance(value, list) else []
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
DEFAULT_MAX_RESPONSE_BYTES: ByteSize = mebibytes(256)
|
|
65
|
+
"""The largest response body (or streamed tool call's parameters) a
|
|
66
|
+
provider client reads by default: room for a large embedding batch, but
|
|
67
|
+
not for a server that never stops sending."""
|
|
68
|
+
|
|
69
|
+
MAX_ERROR_BODY_BYTES: ByteSize = mebibytes(1)
|
|
70
|
+
"""The most of a failed response's body read into its ``AiError``."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def response_limit(max_response_bytes: byte_size.Input | None) -> ByteSize:
|
|
74
|
+
"""``max_response_bytes`` parsed (the default when ``None``); raises
|
|
75
|
+
``ValueError`` when it is not a positive byte size."""
|
|
76
|
+
if max_response_bytes is None:
|
|
77
|
+
return DEFAULT_MAX_RESPONSE_BYTES
|
|
78
|
+
size = byte_size.from_input_unsafe(max_response_bytes)
|
|
79
|
+
if byte_size.is_zero(size):
|
|
80
|
+
raise ValueError("max_response_bytes must be positive")
|
|
81
|
+
return size
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _plain_headers(headers: H.Headers, secret_headers: Sequence[str]) -> dict[str, str]:
|
|
85
|
+
names = (*current_redacted_names(), *secret_headers)
|
|
86
|
+
return {k: str(v) for k, v in headers.redact(names).items()}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def request_details(
|
|
90
|
+
request: HttpClientRequest, *, secret_headers: Sequence[str]
|
|
91
|
+
) -> HttpRequestDetails:
|
|
92
|
+
"""The request as an error or a response part shows it, its secret
|
|
93
|
+
headers redacted: the current redacted names and the provider's
|
|
94
|
+
``secret_headers``, which are named here because a response part is
|
|
95
|
+
built after the client's redaction scope has ended."""
|
|
96
|
+
details: HttpRequestDetails = {
|
|
97
|
+
"method": request.method,
|
|
98
|
+
"url": request.url,
|
|
99
|
+
"urlParams": [(k, str(v)) for k, v in request.url_params],
|
|
100
|
+
"headers": _plain_headers(request.headers, secret_headers),
|
|
101
|
+
}
|
|
102
|
+
if request.hash is not None:
|
|
103
|
+
details["hash"] = request.hash
|
|
104
|
+
return details
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def response_details(response: HttpClientResponse) -> HttpResponseDetails:
|
|
108
|
+
return {"status": response.status, "headers": _plain_headers(response.headers, ())}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def http_context(
|
|
112
|
+
request: HttpClientRequest,
|
|
113
|
+
response: HttpClientResponse | None = None,
|
|
114
|
+
body: str | None = None,
|
|
115
|
+
*,
|
|
116
|
+
secret_headers: Sequence[str],
|
|
117
|
+
) -> HttpContext:
|
|
118
|
+
context: HttpContext = {"request": request_details(request, secret_headers=secret_headers)}
|
|
119
|
+
if response is not None:
|
|
120
|
+
context["response"] = response_details(response)
|
|
121
|
+
if body is not None:
|
|
122
|
+
context["body"] = body
|
|
123
|
+
return context
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def parse_number(raw: str | None) -> int | float | None:
|
|
127
|
+
"""A header's number (Effect's ``Number.parse``), ``None`` when absent
|
|
128
|
+
or not a number."""
|
|
129
|
+
if raw is None or raw.strip() == "":
|
|
130
|
+
return None
|
|
131
|
+
try:
|
|
132
|
+
value = float(raw)
|
|
133
|
+
except ValueError:
|
|
134
|
+
return None
|
|
135
|
+
if math.isnan(value) or math.isinf(value):
|
|
136
|
+
return None
|
|
137
|
+
return int(value) if value.is_integer() else value
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def retry_after(raw: str | None, now_millis: float) -> timedelta | None:
|
|
141
|
+
"""The delay a ``Retry-After`` header asks for; an invalid one is
|
|
142
|
+
absent."""
|
|
143
|
+
if raw is None:
|
|
144
|
+
return None
|
|
145
|
+
parsed = H.parse_retry_after(raw)
|
|
146
|
+
if not isinstance(parsed, Ok):
|
|
147
|
+
return None
|
|
148
|
+
return timedelta(seconds=H.retry_after_delay(parsed.value, now_millis))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def wire_decoder[T](schema: S.Top, _type: type[T]) -> Callable[[object], Effect[T, S.SchemaError]]:
|
|
152
|
+
"""The check of a provider's JSON against ``schema``, giving the JSON
|
|
153
|
+
itself (so fields the schema leaves out stay readable) with its whole
|
|
154
|
+
numbers as ints, as JavaScript reads them: ``1.0`` is an integer to
|
|
155
|
+
Effect's ``Schema.Int``. Every provider wire schema is read through
|
|
156
|
+
this or ``wire_match``."""
|
|
157
|
+
check = cast(
|
|
158
|
+
"Callable[[object], Effect[object, S.SchemaError]]", S.decode_unknown_effect(schema)
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def decode(json: object) -> Effect[T, S.SchemaError]:
|
|
162
|
+
normalized = _json_text.whole_numbers_as_ints(cast("_json_text.Json", json))
|
|
163
|
+
return check(normalized).as_(cast("T", normalized))
|
|
164
|
+
|
|
165
|
+
return decode
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def wire_parser[T](schema: S.Top, _type: type[T]) -> Callable[[object], Effect[T, S.SchemaError]]:
|
|
169
|
+
"""The decoding of a provider's JSON by ``schema``, its whole numbers
|
|
170
|
+
read as ints first (see ``wire_decoder``): unlike ``wire_decoder`` it
|
|
171
|
+
gives the decoded value, so the keys the schema does not declare are
|
|
172
|
+
dropped, as Effect's decoding drops them."""
|
|
173
|
+
decode = cast("Callable[[object], Effect[T, S.SchemaError]]", S.decode_unknown_effect(schema))
|
|
174
|
+
return lambda json: decode(_json_text.whole_numbers_as_ints(cast("_json_text.Json", json)))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def wire_parser_option[T](schema: S.Top, _type: type[T]) -> Callable[[object], T | None]:
|
|
178
|
+
"""``wire_parser``'s decoding, run at once: the decoded value, or
|
|
179
|
+
``None`` when the JSON does not fit ``schema``."""
|
|
180
|
+
decode = S.decode_unknown_option(cast("S.Codec[T, object, Never, object]", schema))
|
|
181
|
+
|
|
182
|
+
def parse(json: object) -> T | None:
|
|
183
|
+
found = decode(_json_text.whole_numbers_as_ints(cast("_json_text.Json", json)))
|
|
184
|
+
return None if found is None else found.value
|
|
185
|
+
|
|
186
|
+
return parse
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def wire_parse_sync[T](schema: S.Top, _type: type[T]) -> Callable[[object], T]:
|
|
190
|
+
"""``wire_parser``'s decoding, run at once: the decoded value; raises
|
|
191
|
+
``SchemaError`` when the JSON does not fit ``schema``."""
|
|
192
|
+
decode = S.decode_unknown_sync(cast("S.Codec[T, object, Never, object]", schema))
|
|
193
|
+
return lambda json: decode(_json_text.whole_numbers_as_ints(cast("_json_text.Json", json)))
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def wire_match[T](schema: S.Top, _type: type[T]) -> Callable[[object], T | None]:
|
|
197
|
+
"""A provider's JSON when it fits ``schema``, its whole numbers as ints
|
|
198
|
+
(see ``wire_decoder``), else ``None``. The JSON is kept as it came,
|
|
199
|
+
undeclared keys included: when the value is shown or sent on, decode
|
|
200
|
+
it with ``wire_parser_option`` instead."""
|
|
201
|
+
is_ = S.is_(schema)
|
|
202
|
+
|
|
203
|
+
def match(json: object) -> T | None:
|
|
204
|
+
normalized = _json_text.whole_numbers_as_ints(cast("_json_text.Json", json))
|
|
205
|
+
return cast("T", normalized) if is_(normalized) else None
|
|
206
|
+
|
|
207
|
+
return match
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def parse_json(text: str | None) -> object:
|
|
211
|
+
"""``text`` as JSON, or ``None``."""
|
|
212
|
+
if text is None:
|
|
213
|
+
return None
|
|
214
|
+
try:
|
|
215
|
+
return parse_json_text(text)
|
|
216
|
+
except ValueError:
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def dumps(value: object) -> str:
|
|
221
|
+
"""JSON as JavaScript's ``JSON.stringify`` writes it."""
|
|
222
|
+
return _json_text.stringify(value)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def with_redacted_headers[A, E, R](
|
|
226
|
+
names: tuple[str, ...],
|
|
227
|
+
) -> Callable[[Effect[A, E, R]], Effect[A, E, R]]:
|
|
228
|
+
"""Run an effect with ``names`` added to the redacted header names."""
|
|
229
|
+
return lambda effect: effect.update_service(
|
|
230
|
+
CurrentRedactedNames, lambda current: (*current, *names)
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@dataclass(frozen=True)
|
|
235
|
+
class ErrorMapper:
|
|
236
|
+
"""A provider's mapping of client failures to ``AiError``: its
|
|
237
|
+
``module`` name and the mapping of a status-code failure."""
|
|
238
|
+
|
|
239
|
+
module: str
|
|
240
|
+
status: Callable[[StatusCodeError, str], Effect[Never, AiError]]
|
|
241
|
+
|
|
242
|
+
def error(self, method: str, reason: Any) -> AiError:
|
|
243
|
+
return AiError(module=self.module, method=method, reason=reason)
|
|
244
|
+
|
|
245
|
+
def schema_error(self, error: S.SchemaError, method: str) -> AiError:
|
|
246
|
+
return self.error(method, InvalidResponseError.from_schema_error(error))
|
|
247
|
+
|
|
248
|
+
def sse_error(self, error: sse.SseError, method: str) -> AiError:
|
|
249
|
+
return self.error(method, InvalidResponseError(description=error.message))
|
|
250
|
+
|
|
251
|
+
def http_error(self, error: HttpClientError, method: str) -> Effect[Never, AiError]:
|
|
252
|
+
reason = error.reason
|
|
253
|
+
match reason:
|
|
254
|
+
case TransportError() | EncodeError() | InvalidUrlError():
|
|
255
|
+
return fx.fail(self.error(method, NetworkError.from_request_error(reason)))
|
|
256
|
+
case StatusCodeError():
|
|
257
|
+
return self.status(reason, method)
|
|
258
|
+
case DecodeError():
|
|
259
|
+
description = or_default(reason.description, "Failed to decode response")
|
|
260
|
+
return fx.fail(self.error(method, InvalidOutputError(description=description)))
|
|
261
|
+
case EmptyBodyError():
|
|
262
|
+
description = or_default(reason.description, "Response body was empty")
|
|
263
|
+
return fx.fail(self.error(method, InvalidOutputError(description=description)))
|
|
264
|
+
|
|
265
|
+
def catch(self, method: str) -> Callable[[object], Effect[Never, AiError]]:
|
|
266
|
+
"""A handler for every failure of a request: client and schema
|
|
267
|
+
errors become ``AiError``, an ``AiError`` passes through."""
|
|
268
|
+
|
|
269
|
+
def handle(error: object) -> Effect[Never, AiError]:
|
|
270
|
+
if isinstance(error, HttpClientError):
|
|
271
|
+
return self.http_error(error, method)
|
|
272
|
+
if isinstance(error, S.SchemaError):
|
|
273
|
+
return fx.fail(self.schema_error(error, method))
|
|
274
|
+
if isinstance(error, sse.SseError):
|
|
275
|
+
return fx.fail(self.sse_error(error, method))
|
|
276
|
+
if isinstance(error, sse.Retry):
|
|
277
|
+
return fx.die(RuntimeError(f"Unexpected SSE retry directive: {error!r}"))
|
|
278
|
+
return fx.fail(error) # pyright: ignore[reportReturnType]
|
|
279
|
+
|
|
280
|
+
return handle
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def limit_responses(
|
|
284
|
+
client: HttpClientService[HttpClientError], max_bytes: ByteSize
|
|
285
|
+
) -> HttpClientService[HttpClientError]:
|
|
286
|
+
"""``client`` reading each response body under a limit: ``max_bytes``
|
|
287
|
+
for a success, ``MAX_ERROR_BODY_BYTES`` for a failure (its body goes
|
|
288
|
+
into an ``AiError``). Every provider request goes through it."""
|
|
289
|
+
|
|
290
|
+
def limit(response: HttpClientResponse) -> HttpClientResponse:
|
|
291
|
+
ok = 200 <= response.status < 300
|
|
292
|
+
return with_body_limit(response, max_bytes if ok else MAX_ERROR_BODY_BYTES)
|
|
293
|
+
|
|
294
|
+
return client.pipe(transform_response(lambda sending: sending.map(limit)))
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def status_body(reason: StatusCodeError) -> Effect[str | None]:
|
|
298
|
+
"""The body of a failed response: its description when that is JSON,
|
|
299
|
+
else the response's text."""
|
|
300
|
+
description = reason.description
|
|
301
|
+
if description and description.startswith("{"):
|
|
302
|
+
return fx.succeed(description)
|
|
303
|
+
return reason.response.text.option().map(
|
|
304
|
+
lambda text: text.value if text is not None and text.value else description
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class TextBuffer:
|
|
309
|
+
"""Text built from streamed deltas (a tool call's parameters), in
|
|
310
|
+
linear time and at most ``max_chars`` characters."""
|
|
311
|
+
|
|
312
|
+
__slots__ = ("_max_chars", "_parts", "_size")
|
|
313
|
+
|
|
314
|
+
def __init__(self, initial: str, max_chars: int) -> None:
|
|
315
|
+
self._parts = [initial] if initial else []
|
|
316
|
+
self._size = len(initial)
|
|
317
|
+
self._max_chars = max_chars
|
|
318
|
+
|
|
319
|
+
def add(self, delta: str) -> bool:
|
|
320
|
+
"""Append ``delta``; ``False`` (and nothing kept) past the limit."""
|
|
321
|
+
if self._size + len(delta) > self._max_chars:
|
|
322
|
+
return False
|
|
323
|
+
self._parts.append(delta)
|
|
324
|
+
self._size += len(delta)
|
|
325
|
+
return True
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def empty(self) -> bool:
|
|
329
|
+
return self._size == 0
|
|
330
|
+
|
|
331
|
+
def text(self) -> str:
|
|
332
|
+
if len(self._parts) > 1:
|
|
333
|
+
self._parts = ["".join(self._parts)]
|
|
334
|
+
return self._parts[0] if self._parts else ""
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def too_large(module: str, method: str, max_chars: int) -> AiError:
|
|
338
|
+
description = f"Streamed tool call parameters exceed {max_chars} characters"
|
|
339
|
+
return AiError(module=module, method=method, reason=InvalidOutputError(description=description))
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def header(headers: Mapping[str, str], name: str) -> str | None:
|
|
343
|
+
return headers.get(name)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def sse_events(
|
|
347
|
+
response: HttpClientResponse,
|
|
348
|
+
) -> Stream[sse.Event, HttpClientError | sse.Retry | sse.SseError]:
|
|
349
|
+
"""The response body read as Server-Sent Events."""
|
|
350
|
+
return response.stream.decode_text().pipe_through_channel(sse.decode())
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
type ClientTransform = Callable[
|
|
354
|
+
[HttpClientService[HttpClientError]], HttpClientService[HttpClientError]
|
|
355
|
+
]
|
|
356
|
+
"""A change to a provider's HTTP client."""
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@dataclass(frozen=True)
|
|
360
|
+
class ClientConfig:
|
|
361
|
+
"""A provider's per-request client overrides (Effect's
|
|
362
|
+
``AnthropicConfig`` / ``OpenAiConfig`` services)."""
|
|
363
|
+
|
|
364
|
+
transform_client: ClientTransform | None = None
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def client_config(tag: type[Tag[ClientConfig]]) -> Effect[ClientConfig | None]:
|
|
368
|
+
"""The provided ``tag`` service, else ``None``."""
|
|
369
|
+
return fx.service_option(tag).map(lambda found: None if found is None else found.value)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def resolve_client(
|
|
373
|
+
tag: type[Tag[ClientConfig]], client: HttpClientService[HttpClientError]
|
|
374
|
+
) -> Effect[HttpClientService[HttpClientError]]:
|
|
375
|
+
"""``client`` through the provided ``tag`` service's transform."""
|
|
376
|
+
return client_config(tag).map(
|
|
377
|
+
lambda config: (
|
|
378
|
+
client
|
|
379
|
+
if config is None or config.transform_client is None
|
|
380
|
+
else config.transform_client(client)
|
|
381
|
+
)
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class WithClientTransform:
|
|
386
|
+
"""Run an effect with ``tag``'s ``transform_client`` set. Use with
|
|
387
|
+
``.pipe``."""
|
|
388
|
+
|
|
389
|
+
__slots__ = ("tag", "transform")
|
|
390
|
+
|
|
391
|
+
def __init__(self, tag: type[Tag[ClientConfig]], transform: ClientTransform) -> None:
|
|
392
|
+
self.tag = tag
|
|
393
|
+
self.transform = transform
|
|
394
|
+
|
|
395
|
+
def __call__[A, E, R](self, effect: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
396
|
+
def run(config: ClientConfig | None) -> Effect[A, E, R]:
|
|
397
|
+
service = replace(config or ClientConfig(), transform_client=self.transform)
|
|
398
|
+
return effect.pipe(fx.provide_service(self.tag, service))
|
|
399
|
+
|
|
400
|
+
return client_config(self.tag).flat_map(run)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class WithConfigOverride[C: Mapping[str, Any]]:
|
|
404
|
+
"""Run an effect with ``overrides`` merged over the provided ``tag``
|
|
405
|
+
config. Use with ``.pipe``."""
|
|
406
|
+
|
|
407
|
+
__slots__ = ("overrides", "tag")
|
|
408
|
+
|
|
409
|
+
def __init__(self, tag: type[Tag[C]], overrides: C) -> None:
|
|
410
|
+
self.tag = tag
|
|
411
|
+
self.overrides = overrides
|
|
412
|
+
|
|
413
|
+
def __call__[A, E, R](self, effect: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
414
|
+
def run(found: Any) -> Effect[A, E, R]:
|
|
415
|
+
current: Mapping[str, Any] = {} if found is None else found.value
|
|
416
|
+
merged = cast("C", {**current, **self.overrides})
|
|
417
|
+
return effect.pipe(fx.provide_service(self.tag, merged))
|
|
418
|
+
|
|
419
|
+
return fx.service_option(self.tag).flat_map(run)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def current_config[C: Mapping[str, Any]](
|
|
423
|
+
tag: type[Tag[C]], *defaults: Mapping[str, Any] | None
|
|
424
|
+
) -> Effect[C]:
|
|
425
|
+
"""``defaults`` merged left to right, then the provided ``tag`` config."""
|
|
426
|
+
|
|
427
|
+
def merge(found: Any) -> C:
|
|
428
|
+
merged: dict[str, Any] = {}
|
|
429
|
+
for config in (*defaults, None if found is None else found.value):
|
|
430
|
+
if config is not None:
|
|
431
|
+
merged.update(config)
|
|
432
|
+
return cast("C", merged)
|
|
433
|
+
|
|
434
|
+
return fx.service_option(tag).map(merge)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def optional_config[A](config: Effect[A, Any] | None) -> Effect[A | None, Any]:
|
|
438
|
+
"""A ``Config`` value, or ``None`` when it was not given."""
|
|
439
|
+
return fx.succeed(None) if config is None else config
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""What the AI providers share when they turn tools into a request and
|
|
2
|
+
tool calls back into parts: the ``UnsupportedInput`` a request builder
|
|
3
|
+
raises, JSON Schemas of tools and response formats, and the
|
|
4
|
+
normalization of a tool call's parameters. ``module`` is the provider's
|
|
5
|
+
language model module name, as its errors show it; ``transformer`` its
|
|
6
|
+
structured-output codec transformer (``to_codec_anthropic``, ...)."""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import pyfect as fx
|
|
13
|
+
from pyfect import schema as S
|
|
14
|
+
from pyfect._core import Effect
|
|
15
|
+
from pyfect.ai import _provider as P
|
|
16
|
+
from pyfect.ai import language_model as lm
|
|
17
|
+
from pyfect.ai import tool as T
|
|
18
|
+
from pyfect.ai.ai_error import AiError, ToolNotFoundError, UnsupportedSchemaError
|
|
19
|
+
from pyfect.json_schema import JsonSchema
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class UnsupportedInput(Exception):
|
|
23
|
+
"""Input a provider cannot take; its ``error`` is the ``AiError`` the
|
|
24
|
+
request fails with. Raised while a request is built and turned into a
|
|
25
|
+
failure where the build is run."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, error: AiError) -> None:
|
|
28
|
+
super().__init__(error.message)
|
|
29
|
+
self.error = error
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def unsupported(module: str, method: str, reason: Any) -> UnsupportedInput:
|
|
33
|
+
return UnsupportedInput(AiError(module=module, method=method, reason=reason))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def unsupported_schema(module: str, error: object, method: str) -> UnsupportedInput:
|
|
37
|
+
return unsupported(module, method, UnsupportedSchemaError(description=str(error)))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def json_schema(
|
|
41
|
+
module: str, schema: Any, method: str, *, transformer: T.CodecTransformer
|
|
42
|
+
) -> P.JsonObject:
|
|
43
|
+
try:
|
|
44
|
+
return dict(T.get_json_schema_from_schema(schema, transformer=transformer))
|
|
45
|
+
except Exception as error:
|
|
46
|
+
raise unsupported_schema(module, error, method) from error
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def memoized(transformer: T.CodecTransformer) -> T.CodecTransformer:
|
|
50
|
+
"""``transformer`` run once per schema: a request prepares its tools'
|
|
51
|
+
codecs once and decodes each of their calls with them."""
|
|
52
|
+
made: dict[int, tuple[S.Top, tuple[S.Top, JsonSchema]]] = {}
|
|
53
|
+
|
|
54
|
+
def transform(schema: S.Top) -> tuple[S.Top, JsonSchema]:
|
|
55
|
+
hit = made.get(id(schema))
|
|
56
|
+
if hit is None:
|
|
57
|
+
hit = (schema, transformer(schema))
|
|
58
|
+
made[id(schema)] = hit
|
|
59
|
+
return hit[1]
|
|
60
|
+
|
|
61
|
+
return transform
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def schema_description(schema: Any) -> str | None:
|
|
65
|
+
description = P.obj(schema.__codec__().ast.annotations).get("description")
|
|
66
|
+
return description if isinstance(description, str) else None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def tool_json_schema(
|
|
70
|
+
module: str, tool: T.AnyTool, *, require_object: bool, transformer: T.CodecTransformer
|
|
71
|
+
) -> P.JsonObject:
|
|
72
|
+
"""The JSON Schema of a tool's parameters (a dynamic tool's own one
|
|
73
|
+
as given); with ``require_object``, anything but an object schema is
|
|
74
|
+
rejected."""
|
|
75
|
+
|
|
76
|
+
def error(cause: object) -> UnsupportedInput:
|
|
77
|
+
return unsupported_schema(
|
|
78
|
+
module,
|
|
79
|
+
f'Tool "{tool.name}" parameters: {cause}. '
|
|
80
|
+
"For a tool without parameters, omit `parameters` or use `Tool.EmptyParams`",
|
|
81
|
+
"prepareTools",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
schema = dict(T.get_json_schema(tool, transformer=transformer))
|
|
86
|
+
except Exception as cause:
|
|
87
|
+
raise error(cause) from cause
|
|
88
|
+
raw = isinstance(tool, T.Dynamic) and tool.json_schema is not None
|
|
89
|
+
if require_object and schema.get("type") != "object" and not raw:
|
|
90
|
+
raise error(f'expected a JSON Schema with type "object", received {P.dumps(schema)}')
|
|
91
|
+
return schema
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def transform_params(
|
|
95
|
+
module: str,
|
|
96
|
+
options: lm.ProviderOptions,
|
|
97
|
+
tool_name: str,
|
|
98
|
+
params: object,
|
|
99
|
+
*,
|
|
100
|
+
transformer: T.CodecTransformer,
|
|
101
|
+
) -> Effect[object, AiError, Any]:
|
|
102
|
+
"""Valid parameters, decoded from the provider's shape by the
|
|
103
|
+
transformer's codec, in the tool's standard encoded form; invalid ones
|
|
104
|
+
unchanged, for the toolkit to report. An unknown tool fails, unless
|
|
105
|
+
tool call validation is deferred (its parameters are kept)."""
|
|
106
|
+
tool = next((t for t in options.tools if t.name == tool_name), None)
|
|
107
|
+
if tool is None:
|
|
108
|
+
if options.tool_call_validation == "deferred":
|
|
109
|
+
return fx.succeed(params)
|
|
110
|
+
return fx.fail(
|
|
111
|
+
AiError(
|
|
112
|
+
module=module,
|
|
113
|
+
method="makeResponse",
|
|
114
|
+
reason=ToolNotFoundError(
|
|
115
|
+
tool_name=tool_name, available_tools=[t.name for t in options.tools]
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
if isinstance(tool, T.Dynamic) and tool.json_schema is not None:
|
|
120
|
+
return fx.succeed(params)
|
|
121
|
+
schema = tool.parameters_schema
|
|
122
|
+
try:
|
|
123
|
+
codec = transformer(schema)[0]
|
|
124
|
+
except Exception as error:
|
|
125
|
+
return fx.fail(unsupported_schema(module, error, "makeResponse").error)
|
|
126
|
+
normalized: Effect[object, Any, Any] = S.decode_unknown_effect(codec)(params).flat_map(
|
|
127
|
+
S.encode_unknown_effect(schema)
|
|
128
|
+
)
|
|
129
|
+
return normalized.or_else_succeed(lambda _: params)
|