qtype 0.0.16__tar.gz → 0.1.1__tar.gz
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.
- {qtype-0.0.16/qtype.egg-info → qtype-0.1.1}/PKG-INFO +16 -3
- {qtype-0.0.16 → qtype-0.1.1}/pyproject.toml +23 -3
- {qtype-0.0.16 → qtype-0.1.1}/qtype/application/commons/tools.py +1 -1
- {qtype-0.0.16 → qtype-0.1.1}/qtype/application/converters/tools_from_api.py +5 -5
- {qtype-0.0.16 → qtype-0.1.1}/qtype/application/converters/tools_from_module.py +2 -2
- qtype-0.1.1/qtype/application/converters/types.py +18 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/application/documentation.py +1 -1
- qtype-0.1.1/qtype/application/facade.py +178 -0
- qtype-0.1.1/qtype/base/types.py +249 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/cli.py +4 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/commands/convert.py +20 -8
- {qtype-0.0.16 → qtype-0.1.1}/qtype/commands/generate.py +19 -27
- {qtype-0.0.16 → qtype-0.1.1}/qtype/commands/run.py +73 -36
- qtype-0.1.1/qtype/commands/serve.py +117 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/commands/validate.py +34 -8
- {qtype-0.0.16 → qtype-0.1.1}/qtype/commands/visualize.py +46 -22
- qtype-0.1.1/qtype/dsl/__init__.py +11 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/dsl/custom_types.py +1 -1
- qtype-0.1.1/qtype/dsl/domain_types.py +119 -0
- qtype-0.1.1/qtype/dsl/linker.py +384 -0
- qtype-0.1.1/qtype/dsl/loader.py +315 -0
- qtype-0.1.1/qtype/dsl/model.py +1242 -0
- qtype-0.1.1/qtype/dsl/parser.py +200 -0
- {qtype-0.0.16/qtype/application/converters → qtype-0.1.1/qtype/dsl}/types.py +8 -5
- qtype-0.1.1/qtype/interpreter/api.py +123 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/auth/aws.py +19 -9
- qtype-0.1.1/qtype/interpreter/auth/generic.py +180 -0
- qtype-0.1.1/qtype/interpreter/base/base_step_executor.py +436 -0
- qtype-0.1.1/qtype/interpreter/base/batch_step_executor.py +171 -0
- qtype-0.1.1/qtype/interpreter/base/exceptions.py +50 -0
- qtype-0.1.1/qtype/interpreter/base/executor_context.py +74 -0
- qtype-0.1.1/qtype/interpreter/base/factory.py +117 -0
- qtype-0.1.1/qtype/interpreter/base/progress_tracker.py +110 -0
- qtype-0.1.1/qtype/interpreter/base/secrets.py +339 -0
- qtype-0.1.1/qtype/interpreter/base/step_cache.py +74 -0
- qtype-0.1.1/qtype/interpreter/base/stream_emitter.py +469 -0
- qtype-0.1.1/qtype/interpreter/conversions.py +606 -0
- qtype-0.1.1/qtype/interpreter/converters.py +77 -0
- qtype-0.1.1/qtype/interpreter/endpoints.py +355 -0
- qtype-0.1.1/qtype/interpreter/executors/agent_executor.py +242 -0
- qtype-0.1.1/qtype/interpreter/executors/aggregate_executor.py +93 -0
- qtype-0.1.1/qtype/interpreter/executors/decoder_executor.py +163 -0
- qtype-0.1.1/qtype/interpreter/executors/doc_to_text_executor.py +112 -0
- qtype-0.1.1/qtype/interpreter/executors/document_embedder_executor.py +107 -0
- qtype-0.1.1/qtype/interpreter/executors/document_search_executor.py +122 -0
- qtype-0.1.1/qtype/interpreter/executors/document_source_executor.py +118 -0
- qtype-0.1.1/qtype/interpreter/executors/document_splitter_executor.py +105 -0
- qtype-0.1.1/qtype/interpreter/executors/echo_executor.py +63 -0
- qtype-0.1.1/qtype/interpreter/executors/field_extractor_executor.py +160 -0
- qtype-0.1.1/qtype/interpreter/executors/file_source_executor.py +101 -0
- qtype-0.1.1/qtype/interpreter/executors/file_writer_executor.py +110 -0
- qtype-0.1.1/qtype/interpreter/executors/index_upsert_executor.py +228 -0
- qtype-0.1.1/qtype/interpreter/executors/invoke_embedding_executor.py +92 -0
- qtype-0.1.1/qtype/interpreter/executors/invoke_flow_executor.py +51 -0
- qtype-0.1.1/qtype/interpreter/executors/invoke_tool_executor.py +358 -0
- qtype-0.1.1/qtype/interpreter/executors/llm_inference_executor.py +272 -0
- qtype-0.1.1/qtype/interpreter/executors/prompt_template_executor.py +78 -0
- qtype-0.1.1/qtype/interpreter/executors/sql_source_executor.py +106 -0
- qtype-0.1.1/qtype/interpreter/executors/vector_search_executor.py +91 -0
- qtype-0.1.1/qtype/interpreter/flow.py +174 -0
- qtype-0.1.1/qtype/interpreter/metadata_api.py +115 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/resource_cache.py +5 -4
- qtype-0.1.1/qtype/interpreter/rich_progress.py +225 -0
- qtype-0.1.1/qtype/interpreter/stream/chat/__init__.py +15 -0
- qtype-0.1.1/qtype/interpreter/stream/chat/converter.py +391 -0
- {qtype-0.0.16/qtype/interpreter → qtype-0.1.1/qtype/interpreter/stream}/chat/file_conversions.py +2 -2
- qtype-0.1.1/qtype/interpreter/stream/chat/ui_request_to_domain_type.py +140 -0
- qtype-0.1.1/qtype/interpreter/stream/chat/vercel.py +609 -0
- qtype-0.1.1/qtype/interpreter/stream/utils/__init__.py +15 -0
- qtype-0.1.1/qtype/interpreter/stream/utils/build_vercel_ai_formatter.py +74 -0
- qtype-0.1.1/qtype/interpreter/stream/utils/callback_to_stream.py +66 -0
- qtype-0.1.1/qtype/interpreter/stream/utils/create_streaming_response.py +18 -0
- qtype-0.1.1/qtype/interpreter/stream/utils/default_chat_extract_text.py +20 -0
- qtype-0.1.1/qtype/interpreter/stream/utils/error_streaming_response.py +20 -0
- qtype-0.1.1/qtype/interpreter/telemetry.py +143 -0
- qtype-0.1.1/qtype/interpreter/tools/__init__.py +5 -0
- qtype-0.1.1/qtype/interpreter/tools/function_tool_helper.py +265 -0
- qtype-0.1.1/qtype/interpreter/types.py +330 -0
- qtype-0.1.1/qtype/interpreter/typing.py +127 -0
- qtype-0.1.1/qtype/interpreter/ui/404/index.html +1 -0
- qtype-0.1.1/qtype/interpreter/ui/404.html +1 -0
- {qtype-0.0.16/qtype/interpreter/ui/_next/static/nUaw6_IwRwPqkzwe5s725 → qtype-0.1.1/qtype/interpreter/ui/_next/static/20HoJN6otZ_LyHLHpCPE6}/_buildManifest.js +1 -1
- qtype-0.0.16/qtype/interpreter/ui/_next/static/chunks/393-8fd474427f8e19ce.js → qtype-0.1.1/qtype/interpreter/ui/_next/static/chunks/434-b2112d19f25c44ff.js +3 -3
- qtype-0.1.1/qtype/interpreter/ui/_next/static/chunks/app/page-8c67d16ac90d23cb.js +1 -0
- qtype-0.1.1/qtype/interpreter/ui/_next/static/chunks/ba12c10f-546f2714ff8abc66.js +1 -0
- qtype-0.1.1/qtype/interpreter/ui/_next/static/css/8a8d1269e362fef7.css +3 -0
- qtype-0.1.1/qtype/interpreter/ui/icon.png +0 -0
- qtype-0.1.1/qtype/interpreter/ui/index.html +1 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/index.txt +4 -4
- qtype-0.1.1/qtype/semantic/checker.py +583 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/semantic/generate.py +262 -83
- qtype-0.1.1/qtype/semantic/loader.py +95 -0
- qtype-0.1.1/qtype/semantic/model.py +799 -0
- qtype-0.1.1/qtype/semantic/resolver.py +141 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/semantic/visualize.py +28 -31
- {qtype-0.0.16 → qtype-0.1.1/qtype.egg-info}/PKG-INFO +16 -3
- {qtype-0.0.16 → qtype-0.1.1}/qtype.egg-info/SOURCES.txt +61 -48
- {qtype-0.0.16 → qtype-0.1.1}/qtype.egg-info/requires.txt +15 -2
- qtype-0.0.16/qtype/application/facade.py +0 -157
- qtype-0.0.16/qtype/base/types.py +0 -29
- qtype-0.0.16/qtype/commands/serve.py +0 -97
- qtype-0.0.16/qtype/dsl/__init__.py +0 -10
- qtype-0.0.16/qtype/dsl/base_types.py +0 -38
- qtype-0.0.16/qtype/dsl/domain_types.py +0 -59
- qtype-0.0.16/qtype/dsl/model.py +0 -993
- qtype-0.0.16/qtype/dsl/validator.py +0 -465
- qtype-0.0.16/qtype/interpreter/api.py +0 -202
- qtype-0.0.16/qtype/interpreter/auth/generic.py +0 -103
- qtype-0.0.16/qtype/interpreter/batch/file_sink_source.py +0 -162
- qtype-0.0.16/qtype/interpreter/batch/flow.py +0 -95
- qtype-0.0.16/qtype/interpreter/batch/sql_source.py +0 -92
- qtype-0.0.16/qtype/interpreter/batch/step.py +0 -74
- qtype-0.0.16/qtype/interpreter/batch/types.py +0 -41
- qtype-0.0.16/qtype/interpreter/batch/utils.py +0 -178
- qtype-0.0.16/qtype/interpreter/chat/chat_api.py +0 -237
- qtype-0.0.16/qtype/interpreter/chat/vercel.py +0 -314
- qtype-0.0.16/qtype/interpreter/conversions.py +0 -166
- qtype-0.0.16/qtype/interpreter/exceptions.py +0 -10
- qtype-0.0.16/qtype/interpreter/flow.py +0 -37
- qtype-0.0.16/qtype/interpreter/step.py +0 -67
- qtype-0.0.16/qtype/interpreter/steps/__init__.py +0 -0
- qtype-0.0.16/qtype/interpreter/steps/agent.py +0 -114
- qtype-0.0.16/qtype/interpreter/steps/condition.py +0 -36
- qtype-0.0.16/qtype/interpreter/steps/decoder.py +0 -88
- qtype-0.0.16/qtype/interpreter/steps/llm_inference.py +0 -171
- qtype-0.0.16/qtype/interpreter/steps/prompt_template.py +0 -54
- qtype-0.0.16/qtype/interpreter/steps/search.py +0 -24
- qtype-0.0.16/qtype/interpreter/steps/tool.py +0 -219
- qtype-0.0.16/qtype/interpreter/streaming_helpers.py +0 -123
- qtype-0.0.16/qtype/interpreter/telemetry.py +0 -16
- qtype-0.0.16/qtype/interpreter/typing.py +0 -133
- qtype-0.0.16/qtype/interpreter/ui/404/index.html +0 -1
- qtype-0.0.16/qtype/interpreter/ui/404.html +0 -1
- qtype-0.0.16/qtype/interpreter/ui/_next/static/chunks/app/page-7e26b6156cfb55d3.js +0 -1
- qtype-0.0.16/qtype/interpreter/ui/_next/static/chunks/ba12c10f-22556063851a6df2.js +0 -1
- qtype-0.0.16/qtype/interpreter/ui/_next/static/css/b40532b0db09cce3.css +0 -3
- qtype-0.0.16/qtype/interpreter/ui/favicon.ico +0 -0
- qtype-0.0.16/qtype/interpreter/ui/index.html +0 -1
- qtype-0.0.16/qtype/loader.py +0 -390
- qtype-0.0.16/qtype/semantic/__init__.py +0 -0
- qtype-0.0.16/qtype/semantic/model.py +0 -522
- qtype-0.0.16/qtype/semantic/resolver.py +0 -97
- qtype-0.0.16/tests/test_auth_cache.py +0 -190
- qtype-0.0.16/tests/test_aws_auth.py +0 -401
- qtype-0.0.16/tests/test_batch_flow.py +0 -311
- qtype-0.0.16/tests/test_batch_step.py +0 -127
- qtype-0.0.16/tests/test_batch_utils.py +0 -251
- qtype-0.0.16/tests/test_documentation.py +0 -36
- qtype-0.0.16/tests/test_dsl_loader.py +0 -706
- qtype-0.0.16/tests/test_dsl_validation.py +0 -165
- qtype-0.0.16/tests/test_file_sink_source.py +0 -287
- qtype-0.0.16/tests/test_generic_auth.py +0 -95
- qtype-0.0.16/tests/test_list_type.py +0 -166
- qtype-0.0.16/tests/test_semantic_generator.py +0 -73
- qtype-0.0.16/tests/test_semantic_resolver.py +0 -82
- qtype-0.0.16/tests/test_sql_source.py +0 -301
- qtype-0.0.16/tests/test_tools_from_module.py +0 -317
- qtype-0.0.16/tests/test_visualize.py +0 -51
- {qtype-0.0.16 → qtype-0.1.1}/LICENSE +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/README.md +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/application/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/application/commons/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/application/converters/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/base/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/base/exceptions.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/base/logging.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/commands/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/auth/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/auth/cache.py +0 -0
- {qtype-0.0.16/qtype/interpreter/ui/_next/static/nUaw6_IwRwPqkzwe5s725 → qtype-0.1.1/qtype/interpreter/ui/_next/static/20HoJN6otZ_LyHLHpCPE6}/_ssgManifest.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/964-2b041321a01cbf56.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/app/_not-found/page-e110d2a9d0a83d82.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/app/layout-a05273ead5de2c41.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/framework-7c95b8e5103c9e90.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/main-app-6fc6346bc8f7f163.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/main-e26b9cb206da2cac.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/pages/_app-0a0020ddd67f79cf.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/pages/_error-03529f2c21436739.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/chunks/webpack-08642e441b39b6c2.js +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/media/747892c23ea88013-s.woff2 +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/media/8d697b304b401681-s.woff2 +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/media/9610d9e46709d722-s.woff2 +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/_next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/file.svg +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/globe.svg +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/next.svg +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/vercel.svg +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/interpreter/ui/window.svg +0 -0
- {qtype-0.0.16/qtype/interpreter/batch → qtype-0.1.1/qtype/semantic}/__init__.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype/semantic/base_types.py +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype.egg-info/dependency_links.txt +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype.egg-info/entry_points.txt +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/qtype.egg-info/top_level.txt +0 -0
- {qtype-0.0.16 → qtype-0.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qtype
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: DSL for Generative AI Prototyping
|
|
5
5
|
Author-email: Lou Kratz <lou.kratz+qtype@bazaarvoice.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -15,20 +15,24 @@ Requires-Dist: python-dotenv>=1.0.0
|
|
|
15
15
|
Requires-Dist: openai>=1.93.0
|
|
16
16
|
Requires-Dist: fsspec>=2025.5.1
|
|
17
17
|
Requires-Dist: mkdocs-awesome-pages-plugin>=2.10.1
|
|
18
|
-
Requires-Dist: mermaid-py>=0.8.0
|
|
19
18
|
Requires-Dist: pip-system-certs>=5.2
|
|
20
19
|
Requires-Dist: openapi3-parser>=1.1.21
|
|
21
20
|
Requires-Dist: pydantic-yaml>=1.6.0
|
|
21
|
+
Requires-Dist: google-cloud-aiplatform>=1.120.0
|
|
22
22
|
Provides-Extra: interpreter
|
|
23
|
+
Requires-Dist: aiostream>=0.7.1; extra == "interpreter"
|
|
23
24
|
Requires-Dist: arize-phoenix-otel>=0.12.1; extra == "interpreter"
|
|
24
25
|
Requires-Dist: boto3>=1.34.0; extra == "interpreter"
|
|
26
|
+
Requires-Dist: docling>=2.55.1; extra == "interpreter"
|
|
27
|
+
Requires-Dist: diskcache[interpreter]>=5.6.3; extra == "interpreter"
|
|
25
28
|
Requires-Dist: fastapi>=0.116.1; extra == "interpreter"
|
|
26
29
|
Requires-Dist: llama-index-embeddings-bedrock>=0.5.2; extra == "interpreter"
|
|
27
30
|
Requires-Dist: llama-index-embeddings-openai>=0.3.1; extra == "interpreter"
|
|
28
|
-
Requires-Dist: llama-index-llms-bedrock-converse>=0.
|
|
31
|
+
Requires-Dist: llama-index-llms-bedrock-converse>=0.10.5; extra == "interpreter"
|
|
29
32
|
Requires-Dist: llama-index-llms-bedrock>=0.3.8; extra == "interpreter"
|
|
30
33
|
Requires-Dist: llama-index>=0.12.45; extra == "interpreter"
|
|
31
34
|
Requires-Dist: openinference-instrumentation-llama-index>=4.3.4; extra == "interpreter"
|
|
35
|
+
Requires-Dist: opensearch-py>=2.7.0; extra == "interpreter"
|
|
32
36
|
Requires-Dist: pandas>=2.2.3; extra == "interpreter"
|
|
33
37
|
Requires-Dist: psycopg2-binary>=2.9.10; extra == "interpreter"
|
|
34
38
|
Requires-Dist: pyarrow>=21.0.0; extra == "interpreter"
|
|
@@ -37,6 +41,15 @@ Requires-Dist: python-magic>=0.4.27; extra == "interpreter"
|
|
|
37
41
|
Requires-Dist: s3fs>=2025.7.0; extra == "interpreter"
|
|
38
42
|
Requires-Dist: sqlalchemy>=2.0.42; extra == "interpreter"
|
|
39
43
|
Requires-Dist: uvicorn[standard]>=0.35.0; extra == "interpreter"
|
|
44
|
+
Requires-Dist: llama-index-llms-vertex>=0.6.1; extra == "interpreter"
|
|
45
|
+
Requires-Dist: langfuse>=3.9.0; extra == "interpreter"
|
|
46
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.35.0; extra == "interpreter"
|
|
47
|
+
Requires-Dist: opentelemetry-sdk>=1.35.0; extra == "interpreter"
|
|
48
|
+
Requires-Dist: docx2txt>=0.9; extra == "interpreter"
|
|
49
|
+
Requires-Dist: llama-index-vector-stores-qdrant>=0.8.6; extra == "interpreter"
|
|
50
|
+
Requires-Dist: jsonpath-ng>=1.7.0; extra == "interpreter"
|
|
51
|
+
Requires-Dist: llama-index-readers-huggingface-fs>=0.4.1; extra == "interpreter"
|
|
52
|
+
Requires-Dist: datasets>=4.4.1; extra == "interpreter"
|
|
40
53
|
Dynamic: license-file
|
|
41
54
|
|
|
42
55
|
# QType
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "qtype"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.1.1"
|
|
4
4
|
description = "DSL for Generative AI Prototyping"
|
|
5
5
|
authors = [{ name="Lou Kratz", email="lou.kratz+qtype@bazaarvoice.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -13,10 +13,10 @@ dependencies = [
|
|
|
13
13
|
"openai>=1.93.0",
|
|
14
14
|
"fsspec>=2025.5.1",
|
|
15
15
|
"mkdocs-awesome-pages-plugin>=2.10.1",
|
|
16
|
-
"mermaid-py>=0.8.0",
|
|
17
16
|
"pip-system-certs>=5.2",
|
|
18
17
|
"openapi3-parser>=1.1.21",
|
|
19
18
|
"pydantic-yaml>=1.6.0",
|
|
19
|
+
"google-cloud-aiplatform>=1.120.0",
|
|
20
20
|
]
|
|
21
21
|
license = "APACHE-2.0"
|
|
22
22
|
license-files = ["LICEN[CS]E*"]
|
|
@@ -26,15 +26,19 @@ Homepage = "https://github.com/bazaarvoice/qtype"
|
|
|
26
26
|
|
|
27
27
|
[project.optional-dependencies]
|
|
28
28
|
interpreter = [
|
|
29
|
+
"aiostream>=0.7.1",
|
|
29
30
|
"arize-phoenix-otel>=0.12.1",
|
|
30
31
|
"boto3>=1.34.0",
|
|
32
|
+
"docling>=2.55.1",
|
|
33
|
+
"diskcache[interpreter]>=5.6.3",
|
|
31
34
|
"fastapi>=0.116.1",
|
|
32
35
|
"llama-index-embeddings-bedrock>=0.5.2",
|
|
33
36
|
"llama-index-embeddings-openai>=0.3.1",
|
|
34
|
-
"llama-index-llms-bedrock-converse>=0.
|
|
37
|
+
"llama-index-llms-bedrock-converse>=0.10.5",
|
|
35
38
|
"llama-index-llms-bedrock>=0.3.8",
|
|
36
39
|
"llama-index>=0.12.45",
|
|
37
40
|
"openinference-instrumentation-llama-index>=4.3.4",
|
|
41
|
+
"opensearch-py>=2.7.0",
|
|
38
42
|
"pandas>=2.2.3",
|
|
39
43
|
"psycopg2-binary>=2.9.10",
|
|
40
44
|
"pyarrow>=21.0.0",
|
|
@@ -43,6 +47,15 @@ interpreter = [
|
|
|
43
47
|
"s3fs>=2025.7.0",
|
|
44
48
|
"sqlalchemy>=2.0.42",
|
|
45
49
|
"uvicorn[standard]>=0.35.0",
|
|
50
|
+
"llama-index-llms-vertex>=0.6.1",
|
|
51
|
+
"langfuse>=3.9.0",
|
|
52
|
+
"opentelemetry-exporter-otlp>=1.35.0",
|
|
53
|
+
"opentelemetry-sdk>=1.35.0",
|
|
54
|
+
"docx2txt>=0.9",
|
|
55
|
+
"llama-index-vector-stores-qdrant>=0.8.6",
|
|
56
|
+
"jsonpath-ng>=1.7.0",
|
|
57
|
+
"llama-index-readers-huggingface-fs>=0.4.1",
|
|
58
|
+
"datasets>=4.4.1",
|
|
46
59
|
]
|
|
47
60
|
|
|
48
61
|
[dependency-groups]
|
|
@@ -52,6 +65,7 @@ dev = [
|
|
|
52
65
|
"coverage>=7.0.0",
|
|
53
66
|
"ipython>=8.37.0",
|
|
54
67
|
"isort>=5.13.0",
|
|
68
|
+
"mermaid-py>=0.8.0",
|
|
55
69
|
"mkdocs-awesome-pages-plugin>=2.10.1",
|
|
56
70
|
"mkdocs-material>=9.6.15",
|
|
57
71
|
"mkdocs>=1.6.1",
|
|
@@ -63,6 +77,7 @@ dev = [
|
|
|
63
77
|
"pkginfo>=1.12.1.2",
|
|
64
78
|
"pre-commit>=3.6.0",
|
|
65
79
|
"pymdown-extensions>=10.16",
|
|
80
|
+
"pytest-asyncio>=1.2.0",
|
|
66
81
|
"pytest-cov>=6.0.0",
|
|
67
82
|
"pytest>=8.4.1",
|
|
68
83
|
"ruff>=0.1.0",
|
|
@@ -79,6 +94,8 @@ docs = [
|
|
|
79
94
|
[tool.uv]
|
|
80
95
|
# Install dev dependencies by default when running uv sync
|
|
81
96
|
default-groups = ["dev"]
|
|
97
|
+
# Install the package itself in editable mode
|
|
98
|
+
package = true
|
|
82
99
|
# Use highest resolution strategy for dependencies
|
|
83
100
|
resolution = "highest"
|
|
84
101
|
# Compile bytecode for faster subsequent runs
|
|
@@ -183,4 +200,7 @@ filterwarnings = [
|
|
|
183
200
|
markers = [
|
|
184
201
|
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
185
202
|
"network: marks tests as requiring network access",
|
|
203
|
+
"asyncio: marks tests as async tests using asyncio",
|
|
186
204
|
]
|
|
205
|
+
# Configure pytest-asyncio to automatically detect async tests
|
|
206
|
+
asyncio_mode = "auto"
|
|
@@ -180,7 +180,7 @@ def parse_duration_string(duration: str) -> int:
|
|
|
180
180
|
|
|
181
181
|
def format_datetime(timestamp: datetime, format_string: str) -> str:
|
|
182
182
|
"""
|
|
183
|
-
Format a timestamp using a custom format string.
|
|
183
|
+
Format a timestamp using a custom format string that can be passed to strftime.
|
|
184
184
|
|
|
185
185
|
Args:
|
|
186
186
|
timestamp: Datetime object to format.
|
|
@@ -17,7 +17,7 @@ from openapi_parser.specification import (
|
|
|
17
17
|
Security,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
from qtype.
|
|
20
|
+
from qtype.base.types import PrimitiveTypeEnum
|
|
21
21
|
from qtype.dsl.model import (
|
|
22
22
|
APIKeyAuthProvider,
|
|
23
23
|
APITool,
|
|
@@ -344,9 +344,9 @@ def to_api_tool(
|
|
|
344
344
|
endpoint=endpoint,
|
|
345
345
|
method=operation.method.value.upper(),
|
|
346
346
|
auth=auth.id if auth else None, # Use auth ID string instead of object
|
|
347
|
-
inputs=inputs
|
|
348
|
-
outputs=outputs
|
|
349
|
-
parameters=parameters
|
|
347
|
+
inputs=inputs,
|
|
348
|
+
outputs=outputs,
|
|
349
|
+
parameters=parameters,
|
|
350
350
|
)
|
|
351
351
|
|
|
352
352
|
|
|
@@ -394,7 +394,7 @@ def to_authorization_provider(
|
|
|
394
394
|
}
|
|
395
395
|
)
|
|
396
396
|
if any(flow.scopes for flow in security.flows.values())
|
|
397
|
-
else
|
|
397
|
+
else [],
|
|
398
398
|
)
|
|
399
399
|
case _:
|
|
400
400
|
raise ValueError(
|
|
@@ -5,7 +5,7 @@ from typing import Any, Type, Union, get_args, get_origin
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
7
|
from qtype.application.converters.types import PYTHON_TYPE_TO_PRIMITIVE_TYPE
|
|
8
|
-
from qtype.
|
|
8
|
+
from qtype.base.types import PrimitiveTypeEnum
|
|
9
9
|
from qtype.dsl.model import (
|
|
10
10
|
CustomType,
|
|
11
11
|
ListType,
|
|
@@ -159,7 +159,7 @@ def _create_tool_from_function(
|
|
|
159
159
|
module_path=func_info["module"],
|
|
160
160
|
function_name=func_name,
|
|
161
161
|
description=description,
|
|
162
|
-
inputs=inputs
|
|
162
|
+
inputs=inputs,
|
|
163
163
|
outputs=outputs,
|
|
164
164
|
)
|
|
165
165
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Re-export type mappings from DSL layer for backward compatibility.
|
|
3
|
+
|
|
4
|
+
This module maintains the application layer's interface while delegating
|
|
5
|
+
to the DSL layer where these mappings are now defined.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from qtype.dsl.types import (
|
|
9
|
+
PRIMITIVE_TO_PYTHON_TYPE,
|
|
10
|
+
PYTHON_TYPE_TO_PRIMITIVE_TYPE,
|
|
11
|
+
python_type_for_list,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"PRIMITIVE_TO_PYTHON_TYPE",
|
|
16
|
+
"PYTHON_TYPE_TO_PRIMITIVE_TYPE",
|
|
17
|
+
"python_type_for_list",
|
|
18
|
+
]
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from typing import Any, Type, Union, get_args, get_origin
|
|
6
6
|
|
|
7
7
|
import qtype.dsl.model as dsl
|
|
8
|
-
from qtype.
|
|
8
|
+
from qtype.base.types import PrimitiveTypeEnum
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _format_type_name(field_type: Any) -> str:
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Main facade for qtype operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from qtype.base.types import PathLike
|
|
10
|
+
from qtype.semantic.model import Application as SemanticApplication
|
|
11
|
+
from qtype.semantic.model import DocumentType as SemanticDocumentType
|
|
12
|
+
|
|
13
|
+
# Note: There should be _zero_ imports here at the top that import qtype.interpreter.
|
|
14
|
+
# That's the whole point of this facade - to avoid importing optional
|
|
15
|
+
# dependencies unless these methods are called.
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class QTypeFacade:
|
|
21
|
+
"""
|
|
22
|
+
Simplified interface for qtype operations.
|
|
23
|
+
|
|
24
|
+
This facade provides lazy-loading wrappers for operations that require
|
|
25
|
+
optional dependencies (interpreter package), allowing base qtype to work
|
|
26
|
+
without those dependencies installed.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def telemetry(self, spec: SemanticDocumentType) -> None:
|
|
30
|
+
if isinstance(spec, SemanticApplication) and spec.telemetry:
|
|
31
|
+
logger.info(
|
|
32
|
+
f"Telemetry enabled with endpoint: {spec.telemetry.endpoint}"
|
|
33
|
+
)
|
|
34
|
+
# Register telemetry if needed
|
|
35
|
+
from qtype.interpreter.telemetry import register
|
|
36
|
+
|
|
37
|
+
register(spec.telemetry, self.secret_manager(spec), spec.id)
|
|
38
|
+
|
|
39
|
+
def secret_manager(self, spec: SemanticDocumentType):
|
|
40
|
+
"""
|
|
41
|
+
Create a secret manager based on the specification.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
spec: SemanticDocumentType specification
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Secret manager instance
|
|
48
|
+
"""
|
|
49
|
+
from qtype.interpreter.base.secrets import create_secret_manager
|
|
50
|
+
|
|
51
|
+
if isinstance(spec, SemanticApplication):
|
|
52
|
+
return create_secret_manager(spec.secret_manager)
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
"Can't create secret manager for non-Application spec"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
async def execute_workflow(
|
|
59
|
+
self,
|
|
60
|
+
path: PathLike,
|
|
61
|
+
inputs: dict | Any,
|
|
62
|
+
flow_name: str | None = None,
|
|
63
|
+
**kwargs: Any,
|
|
64
|
+
) -> Any:
|
|
65
|
+
"""
|
|
66
|
+
Execute a complete workflow from document to results.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
path: Path to the QType specification file
|
|
70
|
+
inputs: Dictionary of input values or DataFrame for batch
|
|
71
|
+
flow_name: Optional name of flow to execute
|
|
72
|
+
**kwargs: Additional dependencies for execution
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
DataFrame with results (one row per input)
|
|
76
|
+
"""
|
|
77
|
+
import pandas as pd
|
|
78
|
+
|
|
79
|
+
from qtype.semantic.loader import load
|
|
80
|
+
|
|
81
|
+
logger.info(f"Executing workflow from {path}")
|
|
82
|
+
|
|
83
|
+
# Load the semantic application
|
|
84
|
+
semantic_model, type_registry = load(Path(path))
|
|
85
|
+
assert isinstance(semantic_model, SemanticApplication)
|
|
86
|
+
self.telemetry(semantic_model)
|
|
87
|
+
|
|
88
|
+
# Find the flow to execute
|
|
89
|
+
if flow_name:
|
|
90
|
+
target_flow = None
|
|
91
|
+
for flow in semantic_model.flows:
|
|
92
|
+
if flow.id == flow_name:
|
|
93
|
+
target_flow = flow
|
|
94
|
+
break
|
|
95
|
+
if target_flow is None:
|
|
96
|
+
raise ValueError(f"Flow '{flow_name}' not found")
|
|
97
|
+
else:
|
|
98
|
+
if semantic_model.flows:
|
|
99
|
+
target_flow = semantic_model.flows[0]
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError("No flows found in application")
|
|
102
|
+
|
|
103
|
+
# Convert inputs to DataFrame (normalize single dict to 1-row DataFrame)
|
|
104
|
+
if isinstance(inputs, dict):
|
|
105
|
+
input_df = pd.DataFrame([inputs])
|
|
106
|
+
elif isinstance(inputs, pd.DataFrame):
|
|
107
|
+
input_df = inputs
|
|
108
|
+
else:
|
|
109
|
+
raise ValueError(
|
|
110
|
+
f"Inputs must be dict or DataFrame, got {type(inputs)}"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Create session
|
|
114
|
+
from qtype.interpreter.converters import (
|
|
115
|
+
dataframe_to_flow_messages,
|
|
116
|
+
flow_messages_to_dataframe,
|
|
117
|
+
)
|
|
118
|
+
from qtype.interpreter.types import Session
|
|
119
|
+
|
|
120
|
+
session = Session(
|
|
121
|
+
session_id=kwargs.pop("session_id", "default"),
|
|
122
|
+
conversation_history=kwargs.pop("conversation_history", []),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Convert DataFrame to FlowMessages
|
|
126
|
+
initial_messages = dataframe_to_flow_messages(input_df, session)
|
|
127
|
+
|
|
128
|
+
# Execute the flow
|
|
129
|
+
from opentelemetry import trace
|
|
130
|
+
|
|
131
|
+
from qtype.interpreter.base.executor_context import ExecutorContext
|
|
132
|
+
from qtype.interpreter.flow import run_flow
|
|
133
|
+
|
|
134
|
+
secret_manager = self.secret_manager(semantic_model)
|
|
135
|
+
context = ExecutorContext(
|
|
136
|
+
secret_manager=secret_manager,
|
|
137
|
+
tracer=trace.get_tracer(__name__),
|
|
138
|
+
)
|
|
139
|
+
results = await run_flow(
|
|
140
|
+
target_flow,
|
|
141
|
+
initial_messages,
|
|
142
|
+
context=context,
|
|
143
|
+
**kwargs,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Convert results back to DataFrame
|
|
147
|
+
results_df = flow_messages_to_dataframe(results, target_flow)
|
|
148
|
+
|
|
149
|
+
return results_df
|
|
150
|
+
|
|
151
|
+
def generate_aws_bedrock_models(self) -> list[dict[str, Any]]:
|
|
152
|
+
"""
|
|
153
|
+
Generate AWS Bedrock model definitions.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
List of model definitions for AWS Bedrock models.
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
ImportError: If boto3 is not installed.
|
|
160
|
+
Exception: If AWS API call fails.
|
|
161
|
+
"""
|
|
162
|
+
import boto3 # type: ignore[import-untyped]
|
|
163
|
+
|
|
164
|
+
logger.info("Discovering AWS Bedrock models...")
|
|
165
|
+
client = boto3.client("bedrock")
|
|
166
|
+
models = client.list_foundation_models()
|
|
167
|
+
|
|
168
|
+
model_definitions = []
|
|
169
|
+
for model_summary in models.get("modelSummaries", []):
|
|
170
|
+
model_definitions.append(
|
|
171
|
+
{
|
|
172
|
+
"id": model_summary["modelId"],
|
|
173
|
+
"provider": "aws-bedrock",
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
logger.info(f"Discovered {len(model_definitions)} AWS Bedrock models")
|
|
178
|
+
return model_definitions
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Common type definitions for qtype."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pathlib
|
|
6
|
+
import types
|
|
7
|
+
import typing
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import (
|
|
10
|
+
Any,
|
|
11
|
+
Generic,
|
|
12
|
+
Literal,
|
|
13
|
+
Optional,
|
|
14
|
+
Type,
|
|
15
|
+
TypeVar,
|
|
16
|
+
Union,
|
|
17
|
+
get_args,
|
|
18
|
+
get_origin,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
from pydantic import ConfigDict as PydanticConfigDict
|
|
23
|
+
from pydantic import Field, model_validator
|
|
24
|
+
|
|
25
|
+
# JSON-serializable value types
|
|
26
|
+
JSONValue = Union[
|
|
27
|
+
str,
|
|
28
|
+
int,
|
|
29
|
+
float,
|
|
30
|
+
bool,
|
|
31
|
+
None,
|
|
32
|
+
dict[str, "JSONValue"],
|
|
33
|
+
list["JSONValue"],
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Path-like type (string or Path object)
|
|
37
|
+
PathLike = Union[str, pathlib.Path]
|
|
38
|
+
|
|
39
|
+
CustomTypeRegistry = dict[str, Type[BaseModel]]
|
|
40
|
+
# Configuration dictionary type
|
|
41
|
+
ConfigDict = dict[str, Any]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------- Shared Base Types and Enums ----------------
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class PrimitiveTypeEnum(str, Enum):
|
|
48
|
+
"""Represents the type of data a user or system input can accept within the DSL."""
|
|
49
|
+
|
|
50
|
+
audio = "audio"
|
|
51
|
+
boolean = "boolean"
|
|
52
|
+
bytes = "bytes"
|
|
53
|
+
citation_document = "citation_document"
|
|
54
|
+
citation_url = "citation_url"
|
|
55
|
+
date = "date"
|
|
56
|
+
datetime = "datetime"
|
|
57
|
+
int = "int"
|
|
58
|
+
file = "file"
|
|
59
|
+
float = "float"
|
|
60
|
+
image = "image"
|
|
61
|
+
text = "text"
|
|
62
|
+
time = "time"
|
|
63
|
+
video = "video"
|
|
64
|
+
thinking = "thinking"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class StepCardinality(str, Enum):
|
|
68
|
+
"""Does this step emit 1 (one) or 0...N (many) items?"""
|
|
69
|
+
|
|
70
|
+
one = "one"
|
|
71
|
+
many = "many"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
ReferenceT = TypeVar("ReferenceT")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Reference(BaseModel, Generic[ReferenceT]):
|
|
78
|
+
"""Represents a reference to another component by its ID."""
|
|
79
|
+
|
|
80
|
+
# model_config = PydanticConfigDict(extra="forbid")
|
|
81
|
+
|
|
82
|
+
ref: str = Field(..., alias="$ref")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _contains_reference_and_str(type_hint: Any) -> bool:
|
|
86
|
+
"""Check if type contains both Reference and str in a union."""
|
|
87
|
+
# Get union args (handles Union, | syntax, and Optional)
|
|
88
|
+
origin = get_origin(type_hint)
|
|
89
|
+
if origin not in (Union, None) and not isinstance(
|
|
90
|
+
type_hint, types.UnionType
|
|
91
|
+
):
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
args = get_args(type_hint)
|
|
95
|
+
if not args:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
has_str = str in args
|
|
99
|
+
has_ref = any(
|
|
100
|
+
get_origin(arg) is Reference
|
|
101
|
+
or (hasattr(arg, "__mro__") and Reference in arg.__mro__)
|
|
102
|
+
for arg in args
|
|
103
|
+
)
|
|
104
|
+
return has_str and has_ref
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _should_transform_field(type_hint: Any) -> tuple[bool, bool]:
|
|
108
|
+
"""
|
|
109
|
+
Check if field should be transformed.
|
|
110
|
+
Returns: (should_transform, is_list)
|
|
111
|
+
"""
|
|
112
|
+
# Check direct union: Reference[T] | str
|
|
113
|
+
if _contains_reference_and_str(type_hint):
|
|
114
|
+
return True, False
|
|
115
|
+
|
|
116
|
+
# Check list of union: list[Reference[T] | str]
|
|
117
|
+
origin = get_origin(type_hint)
|
|
118
|
+
if origin is list:
|
|
119
|
+
args = get_args(type_hint)
|
|
120
|
+
if args and _contains_reference_and_str(args[0]):
|
|
121
|
+
return True, True
|
|
122
|
+
|
|
123
|
+
# Check optional list: list[Reference[T] | str] | None
|
|
124
|
+
if origin is Union or isinstance(type_hint, types.UnionType):
|
|
125
|
+
for arg in get_args(type_hint):
|
|
126
|
+
if get_origin(arg) is list:
|
|
127
|
+
inner_args = get_args(arg)
|
|
128
|
+
if inner_args and _contains_reference_and_str(inner_args[0]):
|
|
129
|
+
return True, True
|
|
130
|
+
|
|
131
|
+
return False, False
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class StrictBaseModel(BaseModel):
|
|
135
|
+
"""Base model with extra fields forbidden."""
|
|
136
|
+
|
|
137
|
+
model_config = PydanticConfigDict(extra="forbid")
|
|
138
|
+
|
|
139
|
+
@model_validator(mode="before")
|
|
140
|
+
@classmethod
|
|
141
|
+
def normalize_string_references(cls, data: Any) -> Any:
|
|
142
|
+
"""
|
|
143
|
+
Normalize string references to Reference objects before validation.
|
|
144
|
+
|
|
145
|
+
Transforms:
|
|
146
|
+
- `field: "ref_id"` -> `field: {"$ref": "ref_id"}`
|
|
147
|
+
- `field: ["ref1", "ref2"]` -> `field: [{"$ref": "ref1"}, {"$ref": "ref2"}]`
|
|
148
|
+
|
|
149
|
+
Only applies to fields typed as `Reference[T] | str` or `list[Reference[T] | str]`.
|
|
150
|
+
"""
|
|
151
|
+
if not isinstance(data, dict):
|
|
152
|
+
return data
|
|
153
|
+
|
|
154
|
+
# Get type hints (evaluates ForwardRefs)
|
|
155
|
+
hints = typing.get_type_hints(cls)
|
|
156
|
+
|
|
157
|
+
# Transform fields
|
|
158
|
+
for field_name, field_value in data.items():
|
|
159
|
+
if field_name == "type" or field_name not in hints:
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
should_transform, is_list = _should_transform_field(
|
|
163
|
+
hints[field_name]
|
|
164
|
+
)
|
|
165
|
+
if not should_transform:
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
if is_list and isinstance(field_value, list):
|
|
169
|
+
data[field_name] = [
|
|
170
|
+
{"$ref": item} if isinstance(item, str) else item
|
|
171
|
+
for item in field_value
|
|
172
|
+
]
|
|
173
|
+
elif not is_list and isinstance(field_value, str):
|
|
174
|
+
data[field_name] = {"$ref": field_value}
|
|
175
|
+
|
|
176
|
+
return data
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class BatchConfig(BaseModel):
|
|
180
|
+
"""Configuration for batch execution.
|
|
181
|
+
|
|
182
|
+
Attributes:
|
|
183
|
+
num_workers: Number of async workers for batch operations.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
batch_size: int = Field(
|
|
187
|
+
default=25,
|
|
188
|
+
description="Max number of rows to send to a step at a time",
|
|
189
|
+
gt=0,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class ConcurrencyConfig(BaseModel):
|
|
194
|
+
"""Configuration for concurrent processing.
|
|
195
|
+
|
|
196
|
+
Attributes:
|
|
197
|
+
num_workers: Number of async workers for batch operations.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
num_workers: int = Field(
|
|
201
|
+
default=1,
|
|
202
|
+
description="Number of async workers for batch operations",
|
|
203
|
+
gt=0,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class BatchableStepMixin(BaseModel):
|
|
208
|
+
"""A mixin for steps that support concurrent batch processing."""
|
|
209
|
+
|
|
210
|
+
batch_config: BatchConfig = Field(
|
|
211
|
+
default_factory=BatchConfig,
|
|
212
|
+
description="Configuration for processing the input stream in batches. If omitted, the step processes items one by one.",
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class CacheConfig(BaseModel):
|
|
217
|
+
directory: PathLike = Field(
|
|
218
|
+
default=pathlib.Path("./.qtype-cache"),
|
|
219
|
+
description="Base cache directory.",
|
|
220
|
+
)
|
|
221
|
+
namespace: Optional[str] = Field(
|
|
222
|
+
default=None, description="Logical namespace for cache keys."
|
|
223
|
+
)
|
|
224
|
+
on_error: Literal["Cache", "Drop"] = "Drop"
|
|
225
|
+
version: str = Field(
|
|
226
|
+
default="1.0", description="Bump to invalidate old cache."
|
|
227
|
+
)
|
|
228
|
+
compress: bool = Field(default=False, description="Compress stored data.")
|
|
229
|
+
ttl: Optional[int] = Field(
|
|
230
|
+
default=None, description="Optional time-to-live in seconds."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class CachedStepMixin(BaseModel):
|
|
235
|
+
"""A mixin for steps that support caching."""
|
|
236
|
+
|
|
237
|
+
cache_config: CacheConfig | None = Field(
|
|
238
|
+
default=None,
|
|
239
|
+
description="Configuration for caching step outputs. If omitted, caching is disabled.",
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class ConcurrentStepMixin(BaseModel):
|
|
244
|
+
"""A mixin for steps that support concurrent processing."""
|
|
245
|
+
|
|
246
|
+
concurrency_config: ConcurrencyConfig = Field(
|
|
247
|
+
default_factory=ConcurrencyConfig,
|
|
248
|
+
description="Configuration for processing the input stream concurrently. If omitted, the step processes items sequentially.",
|
|
249
|
+
)
|