qtype 0.1.11__py3-none-any.whl → 0.1.13__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.
- qtype/` +0 -0
- qtype/application/__init__.py +0 -2
- qtype/application/converters/tools_from_api.py +67 -57
- qtype/application/converters/tools_from_module.py +66 -32
- qtype/base/types.py +6 -1
- qtype/commands/convert.py +3 -6
- qtype/commands/generate.py +97 -10
- qtype/commands/mcp.py +68 -0
- qtype/commands/run.py +116 -44
- qtype/commands/validate.py +4 -4
- qtype/docs/.pages +8 -0
- qtype/docs/Concepts/mental-model-and-philosophy.md +363 -0
- qtype/docs/Contributing/.pages +4 -0
- qtype/docs/Contributing/index.md +283 -0
- qtype/docs/Contributing/roadmap.md +81 -0
- qtype/docs/Decisions/ADR-001-Chat-vs-Completion-Endpoint-Features.md +56 -0
- qtype/docs/Gallery/dataflow_pipelines.md +81 -0
- qtype/docs/Gallery/dataflow_pipelines.mermaid +45 -0
- qtype/docs/Gallery/research_assistant.md +97 -0
- qtype/docs/Gallery/research_assistant.mermaid +42 -0
- qtype/docs/Gallery/simple_chatbot.md +38 -0
- qtype/docs/Gallery/simple_chatbot.mermaid +35 -0
- qtype/docs/How To/Authentication/configure_aws_authentication.md +60 -0
- qtype/docs/How To/Authentication/use_api_key_authentication.md +40 -0
- qtype/docs/How To/Command Line Usage/load_multiple_inputs_from_files.md +77 -0
- qtype/docs/How To/Command Line Usage/pass_inputs_on_the_cli.md +52 -0
- qtype/docs/How To/Command Line Usage/serve_with_auto_reload.md +27 -0
- qtype/docs/How To/Data Processing/adjust_concurrency.md +40 -0
- qtype/docs/How To/Data Processing/cache_step_results.md +71 -0
- qtype/docs/How To/Data Processing/decode_json_xml.md +24 -0
- qtype/docs/How To/Data Processing/explode_collections.md +40 -0
- qtype/docs/How To/Data Processing/gather_results.md +68 -0
- qtype/docs/How To/Data Processing/invoke_other_flows.md +71 -0
- qtype/docs/How To/Data Processing/load_data_from_athena.md +49 -0
- qtype/docs/How To/Data Processing/read_data_from_files.md +61 -0
- qtype/docs/How To/Data Processing/read_sql_databases.md +46 -0
- qtype/docs/How To/Data Processing/write_data_to_file.md +39 -0
- qtype/docs/How To/Invoke Models/call_large_language_models.md +51 -0
- qtype/docs/How To/Invoke Models/create_embeddings.md +49 -0
- qtype/docs/How To/Invoke Models/reuse_prompts_with_templates.md +38 -0
- qtype/docs/How To/Language Features/include_qtype_yaml.md +45 -0
- qtype/docs/How To/Language Features/include_raw_text_from_other_files.md +48 -0
- qtype/docs/How To/Language Features/reference_entities_by_id.md +51 -0
- qtype/docs/How To/Language Features/use_agent_skills.md +29 -0
- qtype/docs/How To/Language Features/use_environment_variables.md +48 -0
- qtype/docs/How To/Language Features/use_optional_variables.md +42 -0
- qtype/docs/How To/Language Features/use_qtype_mcp.md +59 -0
- qtype/docs/How To/Observability & Debugging/trace_calls_with_open_telemetry.md +49 -0
- qtype/docs/How To/Observability & Debugging/validate_qtype_yaml.md +36 -0
- qtype/docs/How To/Observability & Debugging/visualize_application_architecture.md +61 -0
- qtype/docs/How To/Observability & Debugging/visualize_example.mermaid +35 -0
- qtype/docs/How To/Qtype Server/flow_as_ui.png +0 -0
- qtype/docs/How To/Qtype Server/serve_flows_as_apis.md +40 -0
- qtype/docs/How To/Qtype Server/serve_flows_as_ui.md +41 -0
- qtype/docs/How To/Qtype Server/use_conversational_interfaces.md +56 -0
- qtype/docs/How To/Qtype Server/use_variables_with_ui_hints.md +48 -0
- qtype/docs/How To/Tools & Integration/bind_tool_inputs_and_outputs.md +47 -0
- qtype/docs/How To/Tools & Integration/create_tools_from_openapi_specifications.md +85 -0
- qtype/docs/How To/Tools & Integration/create_tools_from_python_modules.md +87 -0
- qtype/docs/Reference/cli.md +336 -0
- qtype/docs/Reference/plugins.md +99 -0
- qtype/docs/Reference/semantic-validation-rules.md +184 -0
- qtype/docs/Tutorials/.pages +1 -0
- qtype/docs/Tutorials/01-first-qtype-application.md +249 -0
- qtype/docs/Tutorials/02-conversational-chatbot.md +327 -0
- qtype/docs/Tutorials/03-structured-data.md +480 -0
- qtype/docs/Tutorials/04-tools-and-function-calling.md +476 -0
- qtype/docs/Tutorials/example_chat.png +0 -0
- qtype/docs/Tutorials/index.md +92 -0
- qtype/docs/components/APIKeyAuthProvider.md +7 -0
- qtype/docs/components/APITool.md +10 -0
- qtype/docs/components/AWSAuthProvider.md +13 -0
- qtype/docs/components/AWSSecretManager.md +5 -0
- qtype/docs/components/Agent.md +6 -0
- qtype/docs/components/Aggregate.md +7 -0
- qtype/docs/components/AggregateStats.md +7 -0
- qtype/docs/components/Application.md +22 -0
- qtype/docs/components/AuthorizationProvider.md +6 -0
- qtype/docs/components/AuthorizationProviderList.md +5 -0
- qtype/docs/components/BearerTokenAuthProvider.md +6 -0
- qtype/docs/components/BedrockReranker.md +8 -0
- qtype/docs/components/ChatContent.md +7 -0
- qtype/docs/components/ChatMessage.md +6 -0
- qtype/docs/components/Collect.md +6 -0
- qtype/docs/components/ConstantPath.md +5 -0
- qtype/docs/components/Construct.md +6 -0
- qtype/docs/components/CustomType.md +7 -0
- qtype/docs/components/Decoder.md +8 -0
- qtype/docs/components/DecoderFormat.md +8 -0
- qtype/docs/components/DocToTextConverter.md +7 -0
- qtype/docs/components/Document.md +7 -0
- qtype/docs/components/DocumentEmbedder.md +6 -0
- qtype/docs/components/DocumentIndex.md +7 -0
- qtype/docs/components/DocumentSearch.md +7 -0
- qtype/docs/components/DocumentSource.md +12 -0
- qtype/docs/components/DocumentSplitter.md +9 -0
- qtype/docs/components/Echo.md +8 -0
- qtype/docs/components/Embedding.md +7 -0
- qtype/docs/components/EmbeddingModel.md +6 -0
- qtype/docs/components/Explode.md +5 -0
- qtype/docs/components/FieldExtractor.md +21 -0
- qtype/docs/components/FileSource.md +6 -0
- qtype/docs/components/FileWriter.md +7 -0
- qtype/docs/components/Flow.md +14 -0
- qtype/docs/components/FlowInterface.md +7 -0
- qtype/docs/components/Index.md +8 -0
- qtype/docs/components/IndexUpsert.md +6 -0
- qtype/docs/components/InvokeEmbedding.md +7 -0
- qtype/docs/components/InvokeFlow.md +8 -0
- qtype/docs/components/InvokeTool.md +8 -0
- qtype/docs/components/LLMInference.md +9 -0
- qtype/docs/components/ListType.md +5 -0
- qtype/docs/components/Memory.md +8 -0
- qtype/docs/components/MessageRole.md +14 -0
- qtype/docs/components/Model.md +10 -0
- qtype/docs/components/ModelList.md +5 -0
- qtype/docs/components/OAuth2AuthProvider.md +9 -0
- qtype/docs/components/PrimitiveTypeEnum.md +20 -0
- qtype/docs/components/PromptTemplate.md +7 -0
- qtype/docs/components/PythonFunctionTool.md +7 -0
- qtype/docs/components/RAGChunk.md +7 -0
- qtype/docs/components/RAGDocument.md +10 -0
- qtype/docs/components/RAGSearchResult.md +8 -0
- qtype/docs/components/Reranker.md +5 -0
- qtype/docs/components/SQLSource.md +8 -0
- qtype/docs/components/Search.md +7 -0
- qtype/docs/components/SearchResult.md +7 -0
- qtype/docs/components/SecretManager.md +7 -0
- qtype/docs/components/SecretReference.md +7 -0
- qtype/docs/components/Source.md +5 -0
- qtype/docs/components/Step.md +8 -0
- qtype/docs/components/TelemetrySink.md +9 -0
- qtype/docs/components/Tool.md +9 -0
- qtype/docs/components/ToolList.md +5 -0
- qtype/docs/components/TypeList.md +5 -0
- qtype/docs/components/Variable.md +8 -0
- qtype/docs/components/VariableList.md +5 -0
- qtype/docs/components/VectorIndex.md +7 -0
- qtype/docs/components/VectorSearch.md +6 -0
- qtype/docs/components/VertexAuthProvider.md +9 -0
- qtype/docs/components/Writer.md +5 -0
- qtype/docs/example_ui.png +0 -0
- qtype/docs/index.md +81 -0
- qtype/docs/legacy_how_tos/.pages +6 -0
- qtype/docs/legacy_how_tos/Configuration/modular-yaml.md +366 -0
- qtype/docs/legacy_how_tos/Configuration/phoenix_projects.png +0 -0
- qtype/docs/legacy_how_tos/Configuration/phoenix_traces.png +0 -0
- qtype/docs/legacy_how_tos/Configuration/reference-by-id.md +251 -0
- qtype/docs/legacy_how_tos/Configuration/telemetry-setup.md +259 -0
- qtype/docs/legacy_how_tos/Data Types/custom-types.md +52 -0
- qtype/docs/legacy_how_tos/Data Types/domain-types.md +113 -0
- qtype/docs/legacy_how_tos/Debugging/visualize-apps.md +147 -0
- qtype/docs/legacy_how_tos/Tools/api-tools.md +29 -0
- qtype/docs/legacy_how_tos/Tools/python-tools.md +299 -0
- qtype/docs/skills/architect/SKILL.md +188 -0
- qtype/docs/skills/architect/references/cheatsheet.md +198 -0
- qtype/docs/skills/architect/references/patterns.md +29 -0
- qtype/docs/stylesheets/extra.css +27 -0
- qtype/dsl/custom_types.py +2 -1
- qtype/dsl/linker.py +23 -7
- qtype/dsl/loader.py +3 -3
- qtype/dsl/model.py +181 -67
- qtype/examples/authentication/aws_authentication.qtype.yaml +63 -0
- qtype/examples/conversational_ai/hello_world_chat.qtype.yaml +43 -0
- qtype/examples/conversational_ai/simple_chatbot.qtype.yaml +40 -0
- qtype/examples/data_processing/athena_query.qtype.yaml +56 -0
- qtype/examples/data_processing/batch_inputs.csv +5 -0
- qtype/examples/data_processing/batch_processing.qtype.yaml +54 -0
- qtype/examples/data_processing/cache_step_results.qtype.yaml +78 -0
- qtype/examples/data_processing/collect_results.qtype.yaml +55 -0
- qtype/examples/data_processing/create_sample_db.py +129 -0
- qtype/examples/data_processing/dataflow_pipelines.qtype.yaml +108 -0
- qtype/examples/data_processing/decode_json.qtype.yaml +23 -0
- qtype/examples/data_processing/explode_items.qtype.yaml +25 -0
- qtype/examples/data_processing/invoke_other_flows.qtype.yaml +98 -0
- qtype/examples/data_processing/read_file.qtype.yaml +60 -0
- qtype/examples/data_processing/reviews.db +0 -0
- qtype/examples/data_processing/sample_article.txt +1 -0
- qtype/examples/data_processing/sample_documents.jsonl +5 -0
- qtype/examples/invoke_models/create_embeddings.qtype.yaml +28 -0
- qtype/examples/invoke_models/simple_llm_call.qtype.yaml +32 -0
- qtype/examples/language_features/include_raw.qtype.yaml +27 -0
- qtype/examples/language_features/optional_variables.qtype.yaml +32 -0
- qtype/examples/language_features/story_prompt.txt +6 -0
- qtype/examples/language_features/ui_hints.qtype.yaml +52 -0
- qtype/examples/legacy/bedrock/data_analysis_with_telemetry.qtype.yaml +169 -0
- qtype/examples/legacy/bedrock/hello_world.qtype.yaml +39 -0
- qtype/examples/legacy/bedrock/hello_world_chat.qtype.yaml +37 -0
- qtype/examples/legacy/bedrock/hello_world_chat_with_telemetry.qtype.yaml +40 -0
- qtype/examples/legacy/bedrock/hello_world_chat_with_thinking.qtype.yaml +40 -0
- qtype/examples/legacy/bedrock/hello_world_completion.qtype.yaml +41 -0
- qtype/examples/legacy/bedrock/hello_world_completion_with_auth.qtype.yaml +44 -0
- qtype/examples/legacy/bedrock/simple_agent_chat.qtype.yaml +46 -0
- qtype/examples/legacy/chat_with_langfuse.qtype.yaml +50 -0
- qtype/examples/legacy/data/customers.csv +6 -0
- qtype/examples/legacy/data_processor.qtype.yaml +48 -0
- qtype/examples/legacy/echo/debug_example.qtype.yaml +59 -0
- qtype/examples/legacy/echo/prompt.qtype.yaml +22 -0
- qtype/examples/legacy/echo/readme.md +29 -0
- qtype/examples/legacy/echo/test.qtype.yaml +26 -0
- qtype/examples/legacy/echo/video.qtype.yaml +20 -0
- qtype/examples/legacy/field_extractor_example.qtype.yaml +137 -0
- qtype/examples/legacy/multi_flow_example.qtype.yaml +125 -0
- qtype/examples/legacy/openai/hello_world_chat.qtype.yaml +43 -0
- qtype/examples/legacy/openai/hello_world_chat_with_telemetry.qtype.yaml +46 -0
- qtype/examples/legacy/qtype_plugin_example.py +51 -0
- qtype/examples/legacy/rag.qtype.yaml +207 -0
- qtype/examples/legacy/sample_data.txt +43 -0
- qtype/examples/legacy/time_utilities.qtype.yaml +64 -0
- qtype/examples/legacy/vertex/README.md +11 -0
- qtype/examples/legacy/vertex/hello_world_chat.qtype.yaml +36 -0
- qtype/examples/legacy/vertex/hello_world_completion.qtype.yaml +40 -0
- qtype/examples/legacy/vertex/hello_world_completion_with_auth.qtype.yaml +45 -0
- qtype/examples/observability_debugging/trace_with_opentelemetry.qtype.yaml +40 -0
- qtype/examples/research_assistant/research_assistant.qtype.yaml +94 -0
- qtype/examples/research_assistant/tavily.oas.yaml +722 -0
- qtype/examples/research_assistant/tavily.qtype.yaml +216 -0
- qtype/examples/tutorials/01_hello_world.qtype.yaml +48 -0
- qtype/examples/tutorials/02_conversational_chat.qtype.yaml +37 -0
- qtype/examples/tutorials/03_structured_data.qtype.yaml +130 -0
- qtype/examples/tutorials/04_tools_and_function_calling.qtype.yaml +89 -0
- qtype/interpreter/api.py +4 -1
- qtype/interpreter/base/base_step_executor.py +3 -1
- qtype/interpreter/base/stream_emitter.py +19 -13
- qtype/interpreter/conversions.py +7 -3
- qtype/interpreter/converters.py +142 -26
- qtype/interpreter/executors/agent_executor.py +2 -3
- qtype/interpreter/executors/aggregate_executor.py +3 -4
- qtype/interpreter/executors/construct_executor.py +15 -15
- qtype/interpreter/executors/doc_to_text_executor.py +1 -3
- qtype/interpreter/executors/field_extractor_executor.py +13 -12
- qtype/interpreter/executors/file_source_executor.py +21 -34
- qtype/interpreter/executors/file_writer_executor.py +4 -4
- qtype/interpreter/executors/index_upsert_executor.py +1 -1
- qtype/interpreter/executors/invoke_embedding_executor.py +1 -4
- qtype/interpreter/executors/invoke_flow_executor.py +2 -2
- qtype/interpreter/executors/invoke_tool_executor.py +19 -18
- qtype/interpreter/executors/llm_inference_executor.py +16 -18
- qtype/interpreter/executors/prompt_template_executor.py +1 -3
- qtype/interpreter/executors/sql_source_executor.py +1 -1
- qtype/interpreter/resource_cache.py +3 -1
- qtype/interpreter/rich_progress.py +6 -3
- qtype/interpreter/stream/chat/converter.py +25 -17
- qtype/interpreter/stream/chat/ui_request_to_domain_type.py +2 -2
- qtype/interpreter/tools/function_tool_helper.py +11 -10
- qtype/interpreter/types.py +89 -4
- qtype/interpreter/typing.py +35 -38
- qtype/mcp/__init__.py +0 -0
- qtype/mcp/server.py +722 -0
- qtype/schema/qtype.schema.json +4016 -0
- qtype/semantic/checker.py +20 -1
- qtype/semantic/generate.py +6 -9
- qtype/semantic/model.py +26 -33
- qtype/semantic/resolver.py +7 -0
- qtype/semantic/visualize.py +45 -53
- {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/METADATA +65 -44
- qtype-0.1.13.dist-info/RECORD +352 -0
- {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/WHEEL +1 -2
- qtype/application/facade.py +0 -177
- qtype-0.1.11.dist-info/RECORD +0 -142
- qtype-0.1.11.dist-info/top_level.txt +0 -1
- {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/entry_points.txt +0 -0
- {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
import threading
|
|
5
5
|
from collections import deque
|
|
6
|
-
from typing import Deque, Dict
|
|
6
|
+
from typing import Any, Deque, Dict
|
|
7
7
|
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.live import Live
|
|
@@ -11,6 +11,7 @@ from rich.panel import Panel
|
|
|
11
11
|
from rich.progress import (
|
|
12
12
|
Progress,
|
|
13
13
|
ProgressColumn,
|
|
14
|
+
TaskID,
|
|
14
15
|
TaskProgressColumn,
|
|
15
16
|
TextColumn,
|
|
16
17
|
TimeElapsedColumn,
|
|
@@ -140,7 +141,7 @@ class RichProgressCallback(ProgressCallback):
|
|
|
140
141
|
)
|
|
141
142
|
|
|
142
143
|
# Map step_id -> Rich task id
|
|
143
|
-
self.tasks:
|
|
144
|
+
self.tasks: dict[str, TaskID] = {}
|
|
144
145
|
self._started = False
|
|
145
146
|
|
|
146
147
|
# Pre-create tasks in the desired order if provided
|
|
@@ -202,7 +203,9 @@ class RichProgressCallback(ProgressCallback):
|
|
|
202
203
|
if total_items is not None:
|
|
203
204
|
update_kwargs["total"] = total_items
|
|
204
205
|
|
|
205
|
-
|
|
206
|
+
from typing import cast
|
|
207
|
+
|
|
208
|
+
self.progress.update(task_id, **cast(Any, update_kwargs))
|
|
206
209
|
|
|
207
210
|
def compute_color(self, items_processed: int, items_in_error: int) -> str:
|
|
208
211
|
# Avoid divide-by-zero
|
|
@@ -258,10 +258,12 @@ class StreamEventConverter:
|
|
|
258
258
|
3. ToolInputAvailableChunk - Complete input ready, tool can execute
|
|
259
259
|
"""
|
|
260
260
|
# 1. Start tool input streaming
|
|
261
|
-
yield ToolInputStartChunk(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
261
|
+
yield ToolInputStartChunk.model_validate(
|
|
262
|
+
{
|
|
263
|
+
"toolCallId": event.tool_call_id,
|
|
264
|
+
"toolName": event.tool_name,
|
|
265
|
+
"providerExecuted": True,
|
|
266
|
+
}
|
|
265
267
|
)
|
|
266
268
|
|
|
267
269
|
# 2. Stream the input as JSON text delta
|
|
@@ -274,11 +276,13 @@ class StreamEventConverter:
|
|
|
274
276
|
)
|
|
275
277
|
|
|
276
278
|
# 3. Signal input is complete and ready for execution
|
|
277
|
-
yield ToolInputAvailableChunk(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
yield ToolInputAvailableChunk.model_validate(
|
|
280
|
+
{
|
|
281
|
+
"toolCallId": event.tool_call_id,
|
|
282
|
+
"toolName": event.tool_name,
|
|
283
|
+
"input": event.tool_input,
|
|
284
|
+
"providerExecuted": True,
|
|
285
|
+
}
|
|
282
286
|
)
|
|
283
287
|
|
|
284
288
|
def _convert_tool_execution_end(
|
|
@@ -289,10 +293,12 @@ class StreamEventConverter:
|
|
|
289
293
|
|
|
290
294
|
Signals successful tool completion with output.
|
|
291
295
|
"""
|
|
292
|
-
yield ToolOutputAvailableChunk(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
+
yield ToolOutputAvailableChunk.model_validate(
|
|
297
|
+
{
|
|
298
|
+
"toolCallId": event.tool_call_id,
|
|
299
|
+
"output": event.tool_output,
|
|
300
|
+
"providerExecuted": True,
|
|
301
|
+
}
|
|
296
302
|
)
|
|
297
303
|
|
|
298
304
|
def _convert_tool_execution_error(
|
|
@@ -303,10 +309,12 @@ class StreamEventConverter:
|
|
|
303
309
|
|
|
304
310
|
Signals tool execution failure with error message.
|
|
305
311
|
"""
|
|
306
|
-
yield ToolOutputErrorChunk(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
312
|
+
yield ToolOutputErrorChunk.model_validate(
|
|
313
|
+
{
|
|
314
|
+
"toolCallId": event.tool_call_id,
|
|
315
|
+
"errorText": event.error_message,
|
|
316
|
+
"providerExecuted": True,
|
|
317
|
+
}
|
|
310
318
|
)
|
|
311
319
|
|
|
312
320
|
def _convert_error(self, event: ErrorEvent) -> Iterator[UIMessageChunk]:
|
|
@@ -39,11 +39,11 @@ def _ui_message_to_domain_type(message: UIMessage) -> ChatMessage:
|
|
|
39
39
|
for part in message.parts:
|
|
40
40
|
if part.type == "text":
|
|
41
41
|
blocks.append(
|
|
42
|
-
ChatContent(type=PrimitiveTypeEnum.text, content=part.text)
|
|
42
|
+
ChatContent(type=PrimitiveTypeEnum.text, content=part.text) # type: ignore[attr-defined]
|
|
43
43
|
)
|
|
44
44
|
elif part.type == "reasoning":
|
|
45
45
|
blocks.append(
|
|
46
|
-
ChatContent(type=PrimitiveTypeEnum.text, content=part.text)
|
|
46
|
+
ChatContent(type=PrimitiveTypeEnum.text, content=part.text) # type: ignore[attr-defined]
|
|
47
47
|
)
|
|
48
48
|
elif part.type == "file":
|
|
49
49
|
blocks.append(
|
|
@@ -14,7 +14,7 @@ from pydantic import create_model
|
|
|
14
14
|
from qtype.base.types import PrimitiveTypeEnum
|
|
15
15
|
from qtype.dsl.model import ListType
|
|
16
16
|
from qtype.dsl.types import PRIMITIVE_TO_PYTHON_TYPE
|
|
17
|
-
from qtype.semantic.model import APITool, PythonFunctionTool,
|
|
17
|
+
from qtype.semantic.model import APITool, PythonFunctionTool, Variable
|
|
18
18
|
|
|
19
19
|
logger = logging.getLogger(__name__)
|
|
20
20
|
|
|
@@ -29,9 +29,9 @@ class FunctionToolHelper:
|
|
|
29
29
|
|
|
30
30
|
@staticmethod
|
|
31
31
|
def _qtype_type_to_python_type(
|
|
32
|
-
param:
|
|
32
|
+
param: Variable,
|
|
33
33
|
) -> type:
|
|
34
|
-
"""Convert QType
|
|
34
|
+
"""Convert QType Variable type to Python type for Pydantic.
|
|
35
35
|
|
|
36
36
|
The param.type has already been resolved during semantic model
|
|
37
37
|
creation, so we just need to convert it to the appropriate Python
|
|
@@ -42,7 +42,7 @@ class FunctionToolHelper:
|
|
|
42
42
|
- Unknown → str
|
|
43
43
|
|
|
44
44
|
Args:
|
|
45
|
-
param: The QType
|
|
45
|
+
param: The QType Variable to convert.
|
|
46
46
|
|
|
47
47
|
Returns:
|
|
48
48
|
Python type suitable for Pydantic field annotation.
|
|
@@ -55,7 +55,8 @@ class FunctionToolHelper:
|
|
|
55
55
|
if isinstance(param.type, ListType):
|
|
56
56
|
# Create a mock parameter with the element type to recursively
|
|
57
57
|
# resolve it
|
|
58
|
-
element_param =
|
|
58
|
+
element_param = Variable(
|
|
59
|
+
id="temp",
|
|
59
60
|
type=param.type.element_type,
|
|
60
61
|
optional=False,
|
|
61
62
|
)
|
|
@@ -74,13 +75,13 @@ class FunctionToolHelper:
|
|
|
74
75
|
@staticmethod
|
|
75
76
|
def _create_fn_schema(
|
|
76
77
|
tool_name: str,
|
|
77
|
-
inputs:
|
|
78
|
+
inputs: list[Variable],
|
|
78
79
|
) -> type[BaseModel] | None:
|
|
79
80
|
"""Create a Pydantic model from QType tool input parameters.
|
|
80
81
|
|
|
81
82
|
Args:
|
|
82
83
|
tool_name: Name of the tool (used for model name).
|
|
83
|
-
inputs:
|
|
84
|
+
inputs: List of input Variables.
|
|
84
85
|
|
|
85
86
|
Returns:
|
|
86
87
|
Pydantic BaseModel class representing the tool's input schema.
|
|
@@ -91,17 +92,17 @@ class FunctionToolHelper:
|
|
|
91
92
|
# Each field is a tuple of (type_annotation, field_info)
|
|
92
93
|
field_definitions: dict[str, Any] = {}
|
|
93
94
|
|
|
94
|
-
for
|
|
95
|
+
for param in inputs:
|
|
95
96
|
python_type = FunctionToolHelper._qtype_type_to_python_type(param)
|
|
96
97
|
|
|
97
98
|
# Create field with optional annotation
|
|
98
99
|
if param.optional:
|
|
99
|
-
field_definitions[
|
|
100
|
+
field_definitions[param.id] = (
|
|
100
101
|
python_type | None, # type: ignore[valid-type]
|
|
101
102
|
PydanticField(default=None),
|
|
102
103
|
)
|
|
103
104
|
else:
|
|
104
|
-
field_definitions[
|
|
105
|
+
field_definitions[param.id] = (
|
|
105
106
|
python_type,
|
|
106
107
|
PydanticField(...),
|
|
107
108
|
)
|
qtype/interpreter/types.py
CHANGED
|
@@ -1,11 +1,38 @@
|
|
|
1
1
|
from typing import Any, Dict, Literal, Optional, Protocol, Union
|
|
2
2
|
|
|
3
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field, model_serializer
|
|
4
4
|
|
|
5
5
|
from qtype.base.types import StrictBaseModel
|
|
6
6
|
from qtype.dsl.domain_types import ChatMessage
|
|
7
7
|
from qtype.semantic.model import Step
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
class _UnsetType:
|
|
11
|
+
"""Sentinel representing an unset variable.
|
|
12
|
+
|
|
13
|
+
Distinguishes between:
|
|
14
|
+
- Variable never mentioned (not in dict)
|
|
15
|
+
- Variable explicitly unset (UNSET value in dict)
|
|
16
|
+
- Variable set to None (None value in dict)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
_instance = None
|
|
20
|
+
|
|
21
|
+
def __new__(cls):
|
|
22
|
+
if cls._instance is None:
|
|
23
|
+
cls._instance = super().__new__(cls)
|
|
24
|
+
return cls._instance
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
return "UNSET"
|
|
28
|
+
|
|
29
|
+
def __bool__(self) -> bool:
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
UNSET = _UnsetType()
|
|
34
|
+
|
|
35
|
+
|
|
9
36
|
# Stream Event Types (Discriminated Union)
|
|
10
37
|
# These events are emitted by executors during flow execution
|
|
11
38
|
# and can be converted to Vercel UI chunks for frontend display
|
|
@@ -293,8 +320,9 @@ class FlowMessage(BaseModel):
|
|
|
293
320
|
"""
|
|
294
321
|
|
|
295
322
|
model_config = ConfigDict(
|
|
296
|
-
frozen=True
|
|
297
|
-
|
|
323
|
+
frozen=True,
|
|
324
|
+
arbitrary_types_allowed=True, # Allow UNSET sentinel
|
|
325
|
+
)
|
|
298
326
|
|
|
299
327
|
session: Session
|
|
300
328
|
variables: Dict[str, Any] = Field(
|
|
@@ -307,6 +335,49 @@ class FlowMessage(BaseModel):
|
|
|
307
335
|
"""Checks if this state has encountered an error."""
|
|
308
336
|
return self.error is not None
|
|
309
337
|
|
|
338
|
+
def is_set(self, var_id: str) -> bool:
|
|
339
|
+
"""Check if a variable is set (not UNSET, may be None)."""
|
|
340
|
+
value = self.variables.get(var_id, UNSET)
|
|
341
|
+
return value is not UNSET
|
|
342
|
+
|
|
343
|
+
def get_variable(self, var_id: str, *, default: Any = UNSET) -> Any:
|
|
344
|
+
"""Get variable value, raising if unset and no default provided.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
var_id: Variable identifier
|
|
348
|
+
default: Value to return if variable is unset. If not provided,
|
|
349
|
+
raises ValueError on unset variables.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Variable value (may be None if explicitly set to None)
|
|
353
|
+
|
|
354
|
+
Raises:
|
|
355
|
+
ValueError: If variable is unset and no default provided
|
|
356
|
+
|
|
357
|
+
Examples:
|
|
358
|
+
# Required variable - throws if unset
|
|
359
|
+
value = message.get_variable("user_input")
|
|
360
|
+
|
|
361
|
+
# Optional variable - returns None if unset
|
|
362
|
+
value = message.get_variable("optional_field", default=None)
|
|
363
|
+
|
|
364
|
+
# Optional with custom default
|
|
365
|
+
value = message.get_variable("count", default=0)
|
|
366
|
+
"""
|
|
367
|
+
value = self.variables.get(var_id, UNSET)
|
|
368
|
+
|
|
369
|
+
if value is UNSET:
|
|
370
|
+
if default is UNSET:
|
|
371
|
+
raise ValueError(
|
|
372
|
+
(
|
|
373
|
+
f"Required variable '{var_id}' is not set. "
|
|
374
|
+
f"Available variables: {list(self.variables.keys())}"
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
return default
|
|
378
|
+
|
|
379
|
+
return value
|
|
380
|
+
|
|
310
381
|
def copy_with_error(self, step_id: str, exc: Exception) -> "FlowMessage":
|
|
311
382
|
"""Returns a copy of this state marked as failed."""
|
|
312
383
|
return self.model_copy(
|
|
@@ -319,15 +390,29 @@ class FlowMessage(BaseModel):
|
|
|
319
390
|
}
|
|
320
391
|
)
|
|
321
392
|
|
|
322
|
-
# It's useful to have copy-on-write style helpers
|
|
323
393
|
def copy_with_variables(
|
|
324
394
|
self, new_variables: dict[str, Any]
|
|
325
395
|
) -> "FlowMessage":
|
|
396
|
+
"""Create a new FlowMessage with updated variables.
|
|
397
|
+
|
|
398
|
+
Note: Can set variables to UNSET to explicitly mark them as unset.
|
|
399
|
+
"""
|
|
326
400
|
new_vars = self.variables.copy()
|
|
327
401
|
new_vars.update(new_variables)
|
|
328
402
|
new_state = self.model_copy(update={"variables": new_vars})
|
|
329
403
|
return new_state
|
|
330
404
|
|
|
405
|
+
@model_serializer
|
|
406
|
+
def serialize_model(self):
|
|
407
|
+
"""Custom serialization that excludes UNSET variables."""
|
|
408
|
+
return {
|
|
409
|
+
"session": self.session,
|
|
410
|
+
"variables": {
|
|
411
|
+
k: v for k, v in self.variables.items() if v is not UNSET
|
|
412
|
+
},
|
|
413
|
+
"error": self.error,
|
|
414
|
+
}
|
|
415
|
+
|
|
331
416
|
|
|
332
417
|
class InterpreterError(Exception):
|
|
333
418
|
"""Base exception class for ProtoGen interpreter errors."""
|
qtype/interpreter/typing.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import uuid
|
|
4
|
-
from typing import Any, Type
|
|
4
|
+
from typing import Any, Type
|
|
5
5
|
|
|
6
|
-
from pydantic import BaseModel, Field,
|
|
6
|
+
from pydantic import BaseModel, Field, create_model
|
|
7
7
|
|
|
8
8
|
from qtype.dsl.model import ListType, PrimitiveTypeEnum
|
|
9
9
|
from qtype.dsl.model import Variable as DSLVariable
|
|
@@ -25,11 +25,9 @@ def _get_variable_type(var: DSLVariable) -> tuple[Type, dict[str, Any]]:
|
|
|
25
25
|
python_type = PRIMITIVE_TO_PYTHON_TYPE.get(var.type, str)
|
|
26
26
|
field_metadata["qtype_type"] = var.type.value
|
|
27
27
|
elif isinstance(var.type, ListType):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
]
|
|
32
|
-
]
|
|
28
|
+
element_var = DSLVariable(id="", type=var.type.element_type)
|
|
29
|
+
element_type, _ = _get_variable_type(element_var)
|
|
30
|
+
python_type = list[element_type] # type: ignore[valid-type]
|
|
33
31
|
field_metadata["qtype_type"] = f"list[{var.type.element_type}]"
|
|
34
32
|
elif (
|
|
35
33
|
isinstance(var.type, type)
|
|
@@ -82,7 +80,7 @@ def create_output_container_type(flow: Flow) -> Type[BaseModel]:
|
|
|
82
80
|
Field(description="List of errored execution outputs"),
|
|
83
81
|
)
|
|
84
82
|
fields["outputs"] = (
|
|
85
|
-
list[output_shape],
|
|
83
|
+
list[output_shape], # type: ignore[valid-type]
|
|
86
84
|
Field(description="List of successful execution outputs"),
|
|
87
85
|
)
|
|
88
86
|
return create_model(f"{flow.id}Response", __base__=BaseModel, **fields) # type: ignore
|
|
@@ -140,37 +138,36 @@ def flow_results_to_output_container(
|
|
|
140
138
|
return output_container(outputs=outputs, errors=errors)
|
|
141
139
|
|
|
142
140
|
|
|
143
|
-
def
|
|
141
|
+
def convert_dict_to_typed_variables(
|
|
142
|
+
data: dict[str, Any], variables: list[Variable]
|
|
143
|
+
) -> dict[str, Any]:
|
|
144
144
|
"""
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
Convert a dictionary of raw values to properly typed variables.
|
|
146
|
+
|
|
147
|
+
Uses Pydantic model validation to convert all values at once based on
|
|
148
|
+
Variable type declarations. This is more efficient than converting each
|
|
149
|
+
field individually.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
data: Dictionary with raw values (e.g., from DataFrame row)
|
|
153
|
+
variables: List of Variable definitions with type information
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Dictionary with values converted to their declared types
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
ValidationError: If values cannot be converted to declared types
|
|
147
160
|
"""
|
|
148
|
-
|
|
161
|
+
# Create a Pydantic model from the variable definitions
|
|
162
|
+
model_class = create_model(
|
|
163
|
+
"TypedVariables",
|
|
164
|
+
__base__=BaseModel,
|
|
165
|
+
**_fields_from_variables(variables),
|
|
166
|
+
)
|
|
149
167
|
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
origin = get_origin(target_type)
|
|
168
|
+
# Validate and convert the data using Pydantic
|
|
169
|
+
validated = model_class.model_validate(data)
|
|
153
170
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return value
|
|
158
|
-
else:
|
|
159
|
-
# It's a generic (list[str], etc.).
|
|
160
|
-
# We skip the identity check and let TypeAdapter handle it.
|
|
161
|
-
pass
|
|
162
|
-
|
|
163
|
-
# 2. Handle Pydantic Models (Custom/Domain Types)
|
|
164
|
-
if hasattr(target_type, "model_validate"):
|
|
165
|
-
return target_type.model_validate(value)
|
|
166
|
-
|
|
167
|
-
# 3. Handle Primitives & Complex Python Types (List, Optional, Union)
|
|
168
|
-
try:
|
|
169
|
-
# TypeAdapter is the "V2 way" to validate things that aren't
|
|
170
|
-
# full Pydantic models (like List[int] or Optional[str])
|
|
171
|
-
return TypeAdapter(target_type).validate_python(value)
|
|
172
|
-
except Exception:
|
|
173
|
-
# Fallback to your original manual cast if TypeAdapter is overkill
|
|
174
|
-
if isinstance(target_type, type):
|
|
175
|
-
return target_type(value)
|
|
176
|
-
raise ValueError(f"Unsupported target type: {target_type}")
|
|
171
|
+
# Return as dict but preserve actual typed instances (not serialized)
|
|
172
|
+
# Use __dict__ to get the actual field values without serialization
|
|
173
|
+
return dict(validated)
|
qtype/mcp/__init__.py
ADDED
|
File without changes
|