qtype 0.1.11__py3-none-any.whl → 0.1.12__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.
- docs/Concepts/mental-model-and-philosophy.md +363 -0
- docs/Contributing/index.md +276 -0
- docs/Contributing/roadmap.md +81 -0
- docs/Decisions/ADR-001-Chat-vs-Completion-Endpoint-Features.md +56 -0
- docs/Gallery/dataflow_pipelines.md +80 -0
- docs/Gallery/dataflow_pipelines.mermaid +45 -0
- docs/Gallery/research_assistant.md +98 -0
- docs/Gallery/research_assistant.mermaid +42 -0
- docs/Gallery/simple_chatbot.md +36 -0
- docs/Gallery/simple_chatbot.mermaid +35 -0
- docs/How To/Authentication/configure_aws_authentication.md +60 -0
- docs/How To/Authentication/use_api_key_authentication.md +40 -0
- docs/How To/Command Line Usage/load_multiple_inputs_from_files.md +62 -0
- docs/How To/Command Line Usage/pass_inputs_on_the_cli.md +52 -0
- docs/How To/Command Line Usage/serve_with_auto_reload.md +26 -0
- docs/How To/Data Processing/adjust_concurrency.md +41 -0
- docs/How To/Data Processing/cache_step_results.md +71 -0
- docs/How To/Data Processing/decode_json_xml.md +24 -0
- docs/How To/Data Processing/explode_collections.md +40 -0
- docs/How To/Data Processing/gather_results.md +68 -0
- docs/How To/Data Processing/read_data_from_files.md +35 -0
- docs/How To/Data Processing/read_sql_databases.md +47 -0
- docs/How To/Data Processing/write_data_to_file.md +40 -0
- docs/How To/Invoke Models/call_large_language_models.md +51 -0
- docs/How To/Invoke Models/create_embeddings.md +49 -0
- docs/How To/Invoke Models/reuse_prompts_with_templates.md +39 -0
- docs/How To/Language Features/include_qtype_yaml.md +45 -0
- docs/How To/Language Features/include_raw_text_from_other_files.md +47 -0
- docs/How To/Language Features/reference_entities_by_id.md +51 -0
- docs/How To/Language Features/use_environment_variables.md +47 -0
- docs/How To/Language Features/use_qtype_mcp.md +59 -0
- docs/How To/Observability & Debugging/trace_calls_with_open_telemetry.md +49 -0
- docs/How To/Observability & Debugging/validate_qtype_yaml.md +35 -0
- docs/How To/Observability & Debugging/visualize_application_architecture.md +61 -0
- docs/How To/Observability & Debugging/visualize_example.mermaid +35 -0
- docs/How To/Qtype Server/flow_as_ui.png +0 -0
- docs/How To/Qtype Server/serve_flows_as_apis.md +40 -0
- docs/How To/Qtype Server/serve_flows_as_ui.md +42 -0
- docs/How To/Qtype Server/use_conversational_interfaces.md +59 -0
- docs/How To/Qtype Server/use_variables_with_ui_hints.md +47 -0
- docs/How To/Tools & Integration/bind_tool_inputs_and_outputs.md +48 -0
- docs/How To/Tools & Integration/create_tools_from_openapi_specifications.md +89 -0
- docs/How To/Tools & Integration/create_tools_from_python_modules.md +90 -0
- docs/Reference/cli.md +338 -0
- docs/Reference/plugins.md +95 -0
- docs/Reference/semantic-validation-rules.md +179 -0
- docs/Tutorials/01-first-qtype-application.md +248 -0
- docs/Tutorials/02-conversational-chatbot.md +327 -0
- docs/Tutorials/03-structured-data.md +481 -0
- docs/Tutorials/04-tools-and-function-calling.md +483 -0
- docs/Tutorials/example_chat.png +0 -0
- docs/Tutorials/index.md +92 -0
- docs/components/APIKeyAuthProvider.md +7 -0
- docs/components/APITool.md +10 -0
- docs/components/AWSAuthProvider.md +13 -0
- docs/components/AWSSecretManager.md +5 -0
- docs/components/Agent.md +6 -0
- docs/components/Aggregate.md +8 -0
- docs/components/AggregateStats.md +7 -0
- docs/components/Application.md +22 -0
- docs/components/AuthorizationProvider.md +6 -0
- docs/components/AuthorizationProviderList.md +5 -0
- docs/components/BearerTokenAuthProvider.md +6 -0
- docs/components/BedrockReranker.md +8 -0
- docs/components/ChatContent.md +7 -0
- docs/components/ChatMessage.md +6 -0
- docs/components/ConstantPath.md +5 -0
- docs/components/CustomType.md +7 -0
- docs/components/Decoder.md +8 -0
- docs/components/DecoderFormat.md +8 -0
- docs/components/DocToTextConverter.md +7 -0
- docs/components/Document.md +7 -0
- docs/components/DocumentEmbedder.md +7 -0
- docs/components/DocumentIndex.md +7 -0
- docs/components/DocumentSearch.md +7 -0
- docs/components/DocumentSource.md +12 -0
- docs/components/DocumentSplitter.md +10 -0
- docs/components/Echo.md +8 -0
- docs/components/Embedding.md +7 -0
- docs/components/EmbeddingModel.md +6 -0
- docs/components/FieldExtractor.md +20 -0
- docs/components/FileSource.md +6 -0
- docs/components/FileWriter.md +7 -0
- docs/components/Flow.md +14 -0
- docs/components/FlowInterface.md +7 -0
- docs/components/Index.md +8 -0
- docs/components/IndexUpsert.md +6 -0
- docs/components/InvokeEmbedding.md +7 -0
- docs/components/InvokeFlow.md +8 -0
- docs/components/InvokeTool.md +8 -0
- docs/components/LLMInference.md +9 -0
- docs/components/ListType.md +5 -0
- docs/components/Memory.md +8 -0
- docs/components/MessageRole.md +14 -0
- docs/components/Model.md +10 -0
- docs/components/ModelList.md +5 -0
- docs/components/OAuth2AuthProvider.md +9 -0
- docs/components/PrimitiveTypeEnum.md +21 -0
- docs/components/PromptTemplate.md +7 -0
- docs/components/PythonFunctionTool.md +7 -0
- docs/components/RAGChunk.md +7 -0
- docs/components/RAGDocument.md +10 -0
- docs/components/RAGSearchResult.md +8 -0
- docs/components/Reranker.md +5 -0
- docs/components/SQLSource.md +8 -0
- docs/components/Search.md +7 -0
- docs/components/SearchResult.md +7 -0
- docs/components/SecretManager.md +7 -0
- docs/components/SecretReference.md +7 -0
- docs/components/Source.md +6 -0
- docs/components/Step.md +9 -0
- docs/components/TelemetrySink.md +9 -0
- docs/components/Tool.md +9 -0
- docs/components/ToolList.md +5 -0
- docs/components/ToolParameter.md +6 -0
- docs/components/TypeList.md +5 -0
- docs/components/Variable.md +6 -0
- docs/components/VariableList.md +5 -0
- docs/components/VectorIndex.md +7 -0
- docs/components/VectorSearch.md +6 -0
- docs/components/VertexAuthProvider.md +9 -0
- docs/components/Writer.md +5 -0
- docs/example_ui.png +0 -0
- docs/index.md +81 -0
- docs/legacy_how_tos/Configuration/modular-yaml.md +366 -0
- docs/legacy_how_tos/Configuration/phoenix_projects.png +0 -0
- docs/legacy_how_tos/Configuration/phoenix_traces.png +0 -0
- docs/legacy_how_tos/Configuration/reference-by-id.md +251 -0
- docs/legacy_how_tos/Configuration/telemetry-setup.md +259 -0
- docs/legacy_how_tos/Data Types/custom-types.md +52 -0
- docs/legacy_how_tos/Data Types/domain-types.md +113 -0
- docs/legacy_how_tos/Debugging/visualize-apps.md +147 -0
- docs/legacy_how_tos/Tools/api-tools.md +29 -0
- docs/legacy_how_tos/Tools/python-tools.md +299 -0
- examples/authentication/aws_authentication.qtype.yaml +63 -0
- examples/conversational_ai/hello_world_chat.qtype.yaml +43 -0
- examples/conversational_ai/simple_chatbot.qtype.yaml +40 -0
- examples/data_processing/batch_processing.qtype.yaml +54 -0
- examples/data_processing/cache_step_results.qtype.yaml +78 -0
- examples/data_processing/collect_results.qtype.yaml +55 -0
- examples/data_processing/dataflow_pipelines.qtype.yaml +108 -0
- examples/data_processing/decode_json.qtype.yaml +23 -0
- examples/data_processing/explode_items.qtype.yaml +25 -0
- examples/data_processing/read_file.qtype.yaml +60 -0
- examples/invoke_models/create_embeddings.qtype.yaml +28 -0
- examples/invoke_models/simple_llm_call.qtype.yaml +32 -0
- examples/language_features/include_raw.qtype.yaml +27 -0
- examples/language_features/ui_hints.qtype.yaml +52 -0
- examples/legacy/bedrock/data_analysis_with_telemetry.qtype.yaml +169 -0
- examples/legacy/bedrock/hello_world.qtype.yaml +39 -0
- examples/legacy/bedrock/hello_world_chat.qtype.yaml +37 -0
- examples/legacy/bedrock/hello_world_chat_with_telemetry.qtype.yaml +40 -0
- examples/legacy/bedrock/hello_world_chat_with_thinking.qtype.yaml +40 -0
- examples/legacy/bedrock/hello_world_completion.qtype.yaml +41 -0
- examples/legacy/bedrock/hello_world_completion_with_auth.qtype.yaml +44 -0
- examples/legacy/bedrock/simple_agent_chat.qtype.yaml +46 -0
- examples/legacy/chat_with_langfuse.qtype.yaml +50 -0
- examples/legacy/data_processor.qtype.yaml +48 -0
- examples/legacy/echo/debug_example.qtype.yaml +59 -0
- examples/legacy/echo/prompt.qtype.yaml +22 -0
- examples/legacy/echo/test.qtype.yaml +26 -0
- examples/legacy/echo/video.qtype.yaml +20 -0
- examples/legacy/field_extractor_example.qtype.yaml +137 -0
- examples/legacy/multi_flow_example.qtype.yaml +125 -0
- examples/legacy/openai/hello_world_chat.qtype.yaml +43 -0
- examples/legacy/openai/hello_world_chat_with_telemetry.qtype.yaml +46 -0
- examples/legacy/rag.qtype.yaml +207 -0
- examples/legacy/time_utilities.qtype.yaml +64 -0
- examples/legacy/vertex/hello_world_chat.qtype.yaml +36 -0
- examples/legacy/vertex/hello_world_completion.qtype.yaml +40 -0
- examples/legacy/vertex/hello_world_completion_with_auth.qtype.yaml +45 -0
- examples/observability_debugging/trace_with_opentelemetry.qtype.yaml +40 -0
- examples/research_assistant/research_assistant.qtype.yaml +94 -0
- examples/research_assistant/tavily.oas.yaml +722 -0
- examples/research_assistant/tavily.qtype.yaml +289 -0
- examples/tutorials/01_hello_world.qtype.yaml +48 -0
- examples/tutorials/02_conversational_chat.qtype.yaml +37 -0
- examples/tutorials/03_structured_data.qtype.yaml +130 -0
- examples/tutorials/04_tools_and_function_calling.qtype.yaml +89 -0
- qtype/application/converters/tools_from_api.py +39 -35
- qtype/base/types.py +6 -1
- qtype/commands/convert.py +3 -6
- qtype/commands/generate.py +7 -3
- qtype/commands/mcp.py +68 -0
- qtype/commands/validate.py +4 -4
- qtype/dsl/custom_types.py +2 -1
- qtype/dsl/linker.py +15 -7
- qtype/dsl/loader.py +3 -3
- qtype/dsl/model.py +24 -3
- qtype/interpreter/api.py +4 -1
- qtype/interpreter/base/base_step_executor.py +3 -1
- qtype/interpreter/conversions.py +7 -3
- qtype/interpreter/executors/construct_executor.py +1 -1
- qtype/interpreter/executors/file_source_executor.py +3 -3
- qtype/interpreter/executors/file_writer_executor.py +4 -4
- qtype/interpreter/executors/index_upsert_executor.py +1 -1
- 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/typing.py +5 -7
- qtype/mcp/__init__.py +0 -0
- qtype/mcp/server.py +467 -0
- qtype/semantic/checker.py +1 -1
- qtype/semantic/generate.py +3 -3
- qtype/semantic/visualize.py +38 -51
- {qtype-0.1.11.dist-info → qtype-0.1.12.dist-info}/METADATA +21 -1
- qtype-0.1.12.dist-info/RECORD +325 -0
- {qtype-0.1.11.dist-info → qtype-0.1.12.dist-info}/WHEEL +1 -1
- schema/qtype.schema.json +4018 -0
- qtype-0.1.11.dist-info/RECORD +0 -142
- {qtype-0.1.11.dist-info → qtype-0.1.12.dist-info}/entry_points.txt +0 -0
- {qtype-0.1.11.dist-info → qtype-0.1.12.dist-info}/licenses/LICENSE +0 -0
- {qtype-0.1.11.dist-info → qtype-0.1.12.dist-info}/top_level.txt +0 -0
qtype/base/types.py
CHANGED
|
@@ -20,7 +20,7 @@ from typing import (
|
|
|
20
20
|
|
|
21
21
|
from pydantic import BaseModel
|
|
22
22
|
from pydantic import ConfigDict as PydanticConfigDict
|
|
23
|
-
from pydantic import Field, model_validator
|
|
23
|
+
from pydantic import Field, model_serializer, model_validator
|
|
24
24
|
|
|
25
25
|
# JSON-serializable value types
|
|
26
26
|
JSONValue = Union[
|
|
@@ -73,6 +73,11 @@ class Reference(BaseModel, Generic[ReferenceT]):
|
|
|
73
73
|
|
|
74
74
|
ref: str = Field(..., alias="$ref")
|
|
75
75
|
|
|
76
|
+
@model_serializer(mode="plain")
|
|
77
|
+
def serialize_as_string(self) -> str:
|
|
78
|
+
"""Serialize Reference as a plain string (just the ID)."""
|
|
79
|
+
return self.ref
|
|
80
|
+
|
|
76
81
|
|
|
77
82
|
def _contains_reference_and_str(type_hint: Any) -> bool:
|
|
78
83
|
"""Check if type contains both Reference and str in a union."""
|
qtype/commands/convert.py
CHANGED
|
@@ -13,7 +13,7 @@ from qtype.dsl.model import Application, Document, ToolList
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def
|
|
16
|
+
def convert_to_yaml(doc: Application | ToolList) -> str:
|
|
17
17
|
"""Convert a document to YAML format."""
|
|
18
18
|
from pydantic_yaml import to_yaml_str
|
|
19
19
|
|
|
@@ -23,9 +23,6 @@ def _convert_to_yaml(doc: Application | ToolList) -> str:
|
|
|
23
23
|
else:
|
|
24
24
|
wrapped = doc
|
|
25
25
|
|
|
26
|
-
import pprint
|
|
27
|
-
|
|
28
|
-
pprint.pprint(wrapped)
|
|
29
26
|
# NOTE: We use exclude_none but NOT exclude_unset because discriminator
|
|
30
27
|
# fields like 'type' have default values and must be included in output
|
|
31
28
|
return to_yaml_str(wrapped, exclude_none=True)
|
|
@@ -54,7 +51,7 @@ def convert_api(args: argparse.Namespace) -> None:
|
|
|
54
51
|
auths=auths,
|
|
55
52
|
)
|
|
56
53
|
# Convert to YAML format
|
|
57
|
-
content =
|
|
54
|
+
content = convert_to_yaml(doc)
|
|
58
55
|
|
|
59
56
|
# Write to file or stdout
|
|
60
57
|
if args.output:
|
|
@@ -96,7 +93,7 @@ def convert_module(args: argparse.Namespace) -> None:
|
|
|
96
93
|
)
|
|
97
94
|
|
|
98
95
|
# Convert to YAML format
|
|
99
|
-
content =
|
|
96
|
+
content = convert_to_yaml(doc)
|
|
100
97
|
|
|
101
98
|
# Write to file or stdout
|
|
102
99
|
if args.output:
|
qtype/commands/generate.py
CHANGED
|
@@ -51,7 +51,11 @@ def run_dump_commons_library(args: argparse.Namespace) -> None:
|
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
# Convert to YAML and save
|
|
54
|
-
|
|
54
|
+
from pydantic_yaml import to_yaml_str
|
|
55
|
+
|
|
56
|
+
content = to_yaml_str(
|
|
57
|
+
model_list, exclude_none=True, exclude_unset=True
|
|
58
|
+
)
|
|
55
59
|
output_path = Path(f"{args.prefix}/aws.bedrock.models.qtype.yaml")
|
|
56
60
|
output_path.write_text(content, encoding="utf-8")
|
|
57
61
|
logger.info(f"AWS Bedrock models exported to {output_path}")
|
|
@@ -118,8 +122,8 @@ def generate_schema(args: argparse.Namespace) -> None:
|
|
|
118
122
|
|
|
119
123
|
schema["$defs"]["qtype_env_var"] = {
|
|
120
124
|
"type": "string",
|
|
121
|
-
"pattern": "^.*\\$\\{[^}:]+(
|
|
122
|
-
"description": "String with environment variable substitution using ${VAR_NAME} or ${VAR_NAME
|
|
125
|
+
"pattern": "^.*\\$\\{[^}:]+(?::-[^}]*)?\\}.*$",
|
|
126
|
+
"description": "String with environment variable substitution using ${VAR_NAME} or ${VAR_NAME:-default} syntax",
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
output = json.dumps(schema, indent=2)
|
qtype/commands/mcp.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for starting the QType MCP server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from qtype.mcp.server import mcp
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main(args: Any) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Start the QType MCP server with the specified transport.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
args: Arguments passed from the command line or calling context.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Update server settings with CLI arguments
|
|
25
|
+
mcp.settings.host = args.host
|
|
26
|
+
mcp.settings.port = args.port
|
|
27
|
+
|
|
28
|
+
logger.info(f"Starting QType MCP server with {args.transport} transport")
|
|
29
|
+
|
|
30
|
+
if args.transport in ("sse", "streamable-http"):
|
|
31
|
+
logger.info(f"Server will be available at {args.host}:{args.port}")
|
|
32
|
+
|
|
33
|
+
# Run the server with the specified transport
|
|
34
|
+
mcp.run(transport=args.transport)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parser(subparsers: argparse._SubParsersAction) -> None:
|
|
38
|
+
"""Set up the mcp subcommand parser.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
subparsers: The subparsers object to add the command to.
|
|
42
|
+
"""
|
|
43
|
+
cmd_parser = subparsers.add_parser(
|
|
44
|
+
"mcp",
|
|
45
|
+
help="Start the QType MCP server for AI agent integration.",
|
|
46
|
+
)
|
|
47
|
+
cmd_parser.add_argument(
|
|
48
|
+
"-t",
|
|
49
|
+
"--transport",
|
|
50
|
+
type=str,
|
|
51
|
+
choices=["stdio", "sse", "streamable-http"],
|
|
52
|
+
default="stdio",
|
|
53
|
+
help="Transport protocol to use (default: stdio)",
|
|
54
|
+
)
|
|
55
|
+
cmd_parser.add_argument(
|
|
56
|
+
"--host",
|
|
57
|
+
type=str,
|
|
58
|
+
default="0.0.0.0",
|
|
59
|
+
help="Host to bind to for HTTP/SSE transports (default: 0.0.0.0)",
|
|
60
|
+
)
|
|
61
|
+
cmd_parser.add_argument(
|
|
62
|
+
"-p",
|
|
63
|
+
"--port",
|
|
64
|
+
type=int,
|
|
65
|
+
default=8000,
|
|
66
|
+
help="Port to bind to for HTTP/SSE transports (default: 8000)",
|
|
67
|
+
)
|
|
68
|
+
cmd_parser.set_defaults(func=main)
|
qtype/commands/validate.py
CHANGED
|
@@ -10,7 +10,7 @@ import sys
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
-
from qtype.base.exceptions import LoadError,
|
|
13
|
+
from qtype.base.exceptions import LoadError, ValidationError
|
|
14
14
|
from qtype.dsl.linker import DuplicateComponentError, ReferenceNotFoundError
|
|
15
15
|
from qtype.dsl.loader import YAMLLoadError
|
|
16
16
|
from qtype.dsl.model import Application as DSLApplication
|
|
@@ -60,9 +60,9 @@ def main(args: Any) -> None:
|
|
|
60
60
|
except ValidationError as e:
|
|
61
61
|
logger.error(f"❌ Validation failed: {e}")
|
|
62
62
|
sys.exit(1)
|
|
63
|
-
except SemanticError as e:
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
# except SemanticError as e:
|
|
64
|
+
# logger.error(f"❌ Semantic validation failed: {e}")
|
|
65
|
+
# sys.exit(1)
|
|
66
66
|
|
|
67
67
|
# If printing is requested, load and print the document
|
|
68
68
|
if args.print:
|
qtype/dsl/custom_types.py
CHANGED
|
@@ -43,7 +43,8 @@ def build_dynamic_types(
|
|
|
43
43
|
resolved_type = ForwardRef(type_str)
|
|
44
44
|
|
|
45
45
|
if is_optional:
|
|
46
|
-
|
|
46
|
+
# Type checker: resolved_type is runtime-constructed type
|
|
47
|
+
return Union[resolved_type, None], True # type: ignore[valid-type]
|
|
47
48
|
|
|
48
49
|
return resolved_type, False
|
|
49
50
|
|
qtype/dsl/linker.py
CHANGED
|
@@ -253,18 +253,26 @@ def _resolve_rootmodel_references(
|
|
|
253
253
|
model: RootModel instance to resolve
|
|
254
254
|
lookup_map: Map of component IDs to objects
|
|
255
255
|
"""
|
|
256
|
-
|
|
257
|
-
if not isinstance(root_list, list):
|
|
258
|
-
return
|
|
256
|
+
root_value = model.root # type: ignore[attr-defined]
|
|
259
257
|
|
|
260
|
-
|
|
258
|
+
def _resolve_root_item(item: Any) -> Any:
|
|
261
259
|
match item:
|
|
262
260
|
case base_types.Reference():
|
|
263
|
-
|
|
264
|
-
item.ref, type(item), lookup_map
|
|
265
|
-
)
|
|
261
|
+
return _resolve_reference(item.ref, type(item), lookup_map)
|
|
266
262
|
case BaseModel():
|
|
267
263
|
_resolve_all_references(item, lookup_map)
|
|
264
|
+
return item
|
|
265
|
+
case _:
|
|
266
|
+
return item
|
|
267
|
+
|
|
268
|
+
# RootModel can wrap either a list (e.g., ToolList) or a single model
|
|
269
|
+
# (e.g., Document -> Application). We need to traverse both.
|
|
270
|
+
if isinstance(root_value, list):
|
|
271
|
+
for i, item in enumerate(root_value):
|
|
272
|
+
root_value[i] = _resolve_root_item(item)
|
|
273
|
+
return
|
|
274
|
+
else:
|
|
275
|
+
model.root = _resolve_root_item(root_value) # type: ignore[attr-defined]
|
|
268
276
|
|
|
269
277
|
|
|
270
278
|
def _resolve_list_references(
|
qtype/dsl/loader.py
CHANGED
|
@@ -6,7 +6,7 @@ This module provides two explicit functions for loading YAML:
|
|
|
6
6
|
- load_yaml_string(content, base_path): Load YAML from a string
|
|
7
7
|
|
|
8
8
|
Both support:
|
|
9
|
-
- Environment variable substitution (${VAR} or ${VAR
|
|
9
|
+
- Environment variable substitution (${VAR} or ${VAR:-default})
|
|
10
10
|
- File inclusion (!include and !include_raw)
|
|
11
11
|
- Multiple URI schemes via fsspec (local, http, s3, etc.)
|
|
12
12
|
|
|
@@ -81,7 +81,7 @@ def _substitute_env_vars(value: str) -> str:
|
|
|
81
81
|
"""
|
|
82
82
|
Substitute environment variables in a string.
|
|
83
83
|
|
|
84
|
-
Supports ${VAR_NAME} or ${VAR_NAME
|
|
84
|
+
Supports ${VAR_NAME} or ${VAR_NAME:-default} syntax.
|
|
85
85
|
|
|
86
86
|
Args:
|
|
87
87
|
value: String containing environment variable references
|
|
@@ -92,7 +92,7 @@ def _substitute_env_vars(value: str) -> str:
|
|
|
92
92
|
Raises:
|
|
93
93
|
ValueError: If required environment variable is not found
|
|
94
94
|
"""
|
|
95
|
-
pattern = r"\$\{([^}:]+)(
|
|
95
|
+
pattern = r"\$\{([^}:]+)(?::-([^}]*))?\}"
|
|
96
96
|
|
|
97
97
|
def replace_env_var(match: re.Match[str]) -> str:
|
|
98
98
|
var_name = match.group(1)
|
qtype/dsl/model.py
CHANGED
|
@@ -285,9 +285,10 @@ class ToolParameter(BaseModel):
|
|
|
285
285
|
@model_serializer
|
|
286
286
|
def _model_serializer(self):
|
|
287
287
|
# Use the default serialization, but ensure 'type' is a string
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
288
|
+
return {
|
|
289
|
+
"type": self._serialize_type(self.type),
|
|
290
|
+
"optional": self.optional,
|
|
291
|
+
}
|
|
291
292
|
|
|
292
293
|
|
|
293
294
|
class ListType(BaseModel):
|
|
@@ -656,6 +657,26 @@ class InvokeTool(Step, ConcurrentStepMixin):
|
|
|
656
657
|
description="Mapping from variable references to tool output parameter names.",
|
|
657
658
|
)
|
|
658
659
|
|
|
660
|
+
@model_validator(mode="after")
|
|
661
|
+
def infer_inputs_outputs_from_bindings(self) -> "InvokeTool":
|
|
662
|
+
def _merge_vars(
|
|
663
|
+
existing: list[Reference[Variable] | str],
|
|
664
|
+
bindings: dict[str, str],
|
|
665
|
+
) -> list[Reference[Variable] | str]:
|
|
666
|
+
"""Merge existing variables with bindings and deduplicate."""
|
|
667
|
+
# NOTE: doesn't handle references. You may duplicate inputs here..
|
|
668
|
+
existing_ids = [item for item in existing if isinstance(item, str)]
|
|
669
|
+
inferred_ids = list(bindings.values())
|
|
670
|
+
merged_ids: list[Reference[Variable] | str] = [
|
|
671
|
+
Reference[Variable].model_validate({"$ref": var_id})
|
|
672
|
+
for var_id in dict.fromkeys(existing_ids + inferred_ids)
|
|
673
|
+
]
|
|
674
|
+
return merged_ids
|
|
675
|
+
|
|
676
|
+
self.inputs = _merge_vars(self.inputs, self.input_bindings)
|
|
677
|
+
self.outputs = _merge_vars(self.outputs, self.output_bindings)
|
|
678
|
+
return self
|
|
679
|
+
|
|
659
680
|
|
|
660
681
|
class InvokeFlow(Step):
|
|
661
682
|
"""Invokes a flow with input and output bindings."""
|
qtype/interpreter/api.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
from contextlib import asynccontextmanager
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
8
|
from fastapi import FastAPI
|
|
8
9
|
from fastapi.middleware.cors import CORSMiddleware
|
|
@@ -86,8 +87,10 @@ class APIExecutor:
|
|
|
86
87
|
if ui_enabled:
|
|
87
88
|
# Add CORS middleware only for localhost development
|
|
88
89
|
if self.host in ("localhost", "127.0.0.1", "0.0.0.0"):
|
|
90
|
+
from typing import cast
|
|
91
|
+
|
|
89
92
|
app.add_middleware(
|
|
90
|
-
CORSMiddleware,
|
|
93
|
+
cast(Any, CORSMiddleware),
|
|
91
94
|
allow_origins=["*"],
|
|
92
95
|
allow_credentials=True,
|
|
93
96
|
allow_methods=["*"],
|
|
@@ -355,7 +355,9 @@ class StepExecutor(ABC):
|
|
|
355
355
|
):
|
|
356
356
|
serialized = [to_cache_value(m, self.step) for m in buf]
|
|
357
357
|
self.cache.set(
|
|
358
|
-
key,
|
|
358
|
+
key,
|
|
359
|
+
serialized,
|
|
360
|
+
expire=self.step.cache_config.ttl, # type: ignore[union-attr]
|
|
359
361
|
) # type: ignore
|
|
360
362
|
|
|
361
363
|
async def _process_message_with_telemetry(
|
qtype/interpreter/conversions.py
CHANGED
|
@@ -331,7 +331,7 @@ def to_embedding_model(
|
|
|
331
331
|
OpenAIEmbedding,
|
|
332
332
|
)
|
|
333
333
|
|
|
334
|
-
api_key = None
|
|
334
|
+
api_key: str | None = None
|
|
335
335
|
if model.auth:
|
|
336
336
|
with auth(model.auth, secret_manager) as provider:
|
|
337
337
|
if not isinstance(provider, APIKeyAuthProvider):
|
|
@@ -343,7 +343,7 @@ def to_embedding_model(
|
|
|
343
343
|
api_key = provider.api_key # type: ignore[assignment]
|
|
344
344
|
|
|
345
345
|
openai_embedding: BaseEmbedding = OpenAIEmbedding(
|
|
346
|
-
api_key=api_key,
|
|
346
|
+
api_key=api_key, # type: ignore[arg-type]
|
|
347
347
|
model_name=model.model_id if model.model_id else model.id,
|
|
348
348
|
)
|
|
349
349
|
return openai_embedding
|
|
@@ -523,7 +523,11 @@ def from_chat_message(message: LlamaChatMessage) -> ChatMessage:
|
|
|
523
523
|
f"Unsupported content block type: {type(block)}"
|
|
524
524
|
)
|
|
525
525
|
|
|
526
|
-
|
|
526
|
+
# Convert llama_index MessageRole to our MessageRole
|
|
527
|
+
from qtype.dsl.domain_types import MessageRole as QTypeMessageRole
|
|
528
|
+
|
|
529
|
+
role = QTypeMessageRole(message.role.value)
|
|
530
|
+
return ChatMessage(role=role, blocks=blocks)
|
|
527
531
|
|
|
528
532
|
|
|
529
533
|
def to_text_splitter(splitter: DocumentSplitter) -> Any:
|
|
@@ -53,7 +53,7 @@ class ConstructExecutor(StepExecutor):
|
|
|
53
53
|
}
|
|
54
54
|
# use the mapping to convert variable names to
|
|
55
55
|
inputs = {
|
|
56
|
-
self.step.field_mapping.get(var_name, var_name): value
|
|
56
|
+
self.step.field_mapping.get(var_name, var_name): value # type: ignore[attr-defined]
|
|
57
57
|
for var_name, value in input_values.items()
|
|
58
58
|
}
|
|
59
59
|
else:
|
|
@@ -40,10 +40,10 @@ class FileSourceExecutor(StepExecutor):
|
|
|
40
40
|
output_columns = {output.id for output in self.step.outputs}
|
|
41
41
|
|
|
42
42
|
# get the path
|
|
43
|
-
if isinstance(self.step.path, ConstantPath):
|
|
44
|
-
file_path = self.step.path
|
|
43
|
+
if isinstance(self.step.path, ConstantPath): # type: ignore[attr-defined]
|
|
44
|
+
file_path = self.step.path # type: ignore[attr-defined]
|
|
45
45
|
else:
|
|
46
|
-
file_path = message.variables.get(self.step.path.id)
|
|
46
|
+
file_path = message.variables.get(self.step.path.id) # type: ignore[attr-defined]
|
|
47
47
|
if not file_path:
|
|
48
48
|
raise ValueError(
|
|
49
49
|
(
|
|
@@ -48,8 +48,8 @@ class FileWriterExecutor(BatchedStepExecutor):
|
|
|
48
48
|
if len(self.step.outputs):
|
|
49
49
|
output_name = self.step.outputs[0].id
|
|
50
50
|
|
|
51
|
-
if isinstance(self.step.path, ConstantPath):
|
|
52
|
-
file_path = self.step.path.uri
|
|
51
|
+
if isinstance(self.step.path, ConstantPath): # type: ignore[attr-defined]
|
|
52
|
+
file_path = self.step.path.uri # type: ignore[attr-defined]
|
|
53
53
|
df = self.to_pandas(batch)
|
|
54
54
|
# A fixed path is provided -- just write all of the data
|
|
55
55
|
await self.stream_emitter.status(
|
|
@@ -67,12 +67,12 @@ class FileWriterExecutor(BatchedStepExecutor):
|
|
|
67
67
|
|
|
68
68
|
else:
|
|
69
69
|
# Group messages by file path (path is a Variable in this branch)
|
|
70
|
-
if not isinstance(self.step.path, Variable):
|
|
70
|
+
if not isinstance(self.step.path, Variable): # type: ignore[attr-defined]
|
|
71
71
|
raise ValueError(
|
|
72
72
|
"Expected path to be a Variable in dynamic path case."
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
-
path_var_id = self.step.path.id
|
|
75
|
+
path_var_id = self.step.path.id # type: ignore[attr-defined]
|
|
76
76
|
|
|
77
77
|
# Sort messages by file path for groupby
|
|
78
78
|
sorted_batch = sorted(
|
|
@@ -145,7 +145,7 @@ class IndexUpsertExecutor(BatchedStepExecutor):
|
|
|
145
145
|
nodes.append(node)
|
|
146
146
|
|
|
147
147
|
# Batch upsert all nodes to the vector store
|
|
148
|
-
await self._vector_store.async_add(nodes)
|
|
148
|
+
await self._vector_store.async_add(nodes) # type: ignore[union-attr]
|
|
149
149
|
num_inserted = len(items)
|
|
150
150
|
|
|
151
151
|
# Emit status update
|
|
@@ -41,7 +41,7 @@ class SQLSourceExecutor(StepExecutor):
|
|
|
41
41
|
connection_string = self._resolve_secret(self.step.connection)
|
|
42
42
|
connect_args = {}
|
|
43
43
|
if self.step.auth:
|
|
44
|
-
with auth(self.step.auth) as creds:
|
|
44
|
+
with auth(self.step.auth, self._secret_manager) as creds:
|
|
45
45
|
if isinstance(creds, boto3.Session):
|
|
46
46
|
connect_args["session"] = creds
|
|
47
47
|
engine = create_engine(connection_string, connect_args=connect_args)
|
|
@@ -25,9 +25,11 @@ def cached_resource(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
|
25
25
|
|
|
26
26
|
@functools.wraps(func)
|
|
27
27
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
28
|
+
# Use getattr with fallback for callables that aren't functions
|
|
29
|
+
qualname = getattr(func, "__qualname__", repr(func))
|
|
28
30
|
cache_key = (
|
|
29
31
|
func.__module__,
|
|
30
|
-
|
|
32
|
+
qualname,
|
|
31
33
|
args,
|
|
32
34
|
tuple(sorted(kwargs.items())),
|
|
33
35
|
)
|
|
@@ -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(
|
qtype/interpreter/typing.py
CHANGED
|
@@ -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
|
|
@@ -162,7 +160,7 @@ def instantiate_variable(variable: DSLVariable, value: Any) -> Any:
|
|
|
162
160
|
|
|
163
161
|
# 2. Handle Pydantic Models (Custom/Domain Types)
|
|
164
162
|
if hasattr(target_type, "model_validate"):
|
|
165
|
-
return target_type.model_validate(value)
|
|
163
|
+
return target_type.model_validate(value) # type: ignore[misc]
|
|
166
164
|
|
|
167
165
|
# 3. Handle Primitives & Complex Python Types (List, Optional, Union)
|
|
168
166
|
try:
|
qtype/mcp/__init__.py
ADDED
|
File without changes
|