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/mcp/server.py
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from importlib.resources import files
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from mcp.server.fastmcp import FastMCP
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
from qtype.commands.convert import convert_to_yaml
|
|
14
|
+
|
|
15
|
+
# Initialize FastMCP server
|
|
16
|
+
mcp = FastMCP("qtype", host="0.0.0.0")
|
|
17
|
+
|
|
18
|
+
# Regex for pymdownx snippets: --8<-- "path/to/file"
|
|
19
|
+
SNIPPET_REGEX = re.compile(r'--8<--\s+"([^"]+)"')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ============================================================================
|
|
23
|
+
# Helper Functions
|
|
24
|
+
# ============================================================================
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_docs_path() -> Path:
|
|
28
|
+
"""Get the path to the documentation directory.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Path to the docs directory, trying installed package first,
|
|
32
|
+
then falling back to development path.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
# Try to get from installed package
|
|
36
|
+
docs_root = files("qtype") / "docs"
|
|
37
|
+
# Check if it exists by trying to iterate
|
|
38
|
+
list(docs_root.iterdir())
|
|
39
|
+
return Path(str(docs_root))
|
|
40
|
+
except (FileNotFoundError, AttributeError, TypeError):
|
|
41
|
+
# Fall back to development path
|
|
42
|
+
return Path(__file__).parent.parent.parent / "docs"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@lru_cache(maxsize=1)
|
|
46
|
+
def _load_schema() -> dict[str, Any]:
|
|
47
|
+
"""Load the QType schema JSON file once and cache it.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The complete schema as a dictionary.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
FileNotFoundError: If schema file doesn't exist.
|
|
54
|
+
json.JSONDecodeError: If schema file is invalid JSON.
|
|
55
|
+
"""
|
|
56
|
+
# Try to load from installed package data first
|
|
57
|
+
try:
|
|
58
|
+
schema_file = files("qtype") / "qtype.schema.json"
|
|
59
|
+
schema_text = schema_file.read_text(encoding="utf-8")
|
|
60
|
+
return json.loads(schema_text)
|
|
61
|
+
except (FileNotFoundError, AttributeError):
|
|
62
|
+
# Fall back to development path
|
|
63
|
+
schema_path = (
|
|
64
|
+
Path(__file__).parent.parent.parent / "schema/qtype.schema.json"
|
|
65
|
+
)
|
|
66
|
+
with open(schema_path, encoding="utf-8") as f:
|
|
67
|
+
return json.load(f)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def resolve_snippets(content: str, base_path: Path) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Recursively finds and replaces MkDocs snippets in markdown content.
|
|
73
|
+
Mimics the behavior of pymdownx.snippets.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
content: The markdown content to process
|
|
77
|
+
base_path: Path to the file being processed (used to resolve relative paths)
|
|
78
|
+
"""
|
|
79
|
+
docs_root = _get_docs_path()
|
|
80
|
+
project_root = docs_root.parent
|
|
81
|
+
|
|
82
|
+
def replace_match(match):
|
|
83
|
+
snippet_path = match.group(1)
|
|
84
|
+
|
|
85
|
+
# pymdownx logic: try relative to current file, then relative to docs, then project root
|
|
86
|
+
candidates = [
|
|
87
|
+
base_path.parent / snippet_path, # Relative to the doc file
|
|
88
|
+
docs_root / snippet_path, # Relative to docs root
|
|
89
|
+
project_root / snippet_path, # Relative to project root
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
for candidate in candidates:
|
|
93
|
+
if candidate.exists() and candidate.is_file():
|
|
94
|
+
# Recursively resolve snippets inside the included file
|
|
95
|
+
return resolve_snippets(
|
|
96
|
+
candidate.read_text(encoding="utf-8"), candidate
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return f"> [!WARNING] Could not resolve snippet: {snippet_path}"
|
|
100
|
+
|
|
101
|
+
return SNIPPET_REGEX.sub(replace_match, content)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ============================================================================
|
|
105
|
+
# Tool Functions
|
|
106
|
+
# ============================================================================
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class MermaidDiagramPreviewInput(BaseModel):
|
|
110
|
+
"""Arguments for VS Code's `mermaid-diagram-preview` tool."""
|
|
111
|
+
|
|
112
|
+
code: str
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class MermaidVisualizationResult(BaseModel):
|
|
116
|
+
"""Structured output for Mermaid visualization."""
|
|
117
|
+
|
|
118
|
+
mermaid_code: str
|
|
119
|
+
mermaid_markdown: str
|
|
120
|
+
suggested_next_tool: str
|
|
121
|
+
mermaid_diagram_preview_input: MermaidDiagramPreviewInput
|
|
122
|
+
preview_instructions: str
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@mcp.tool(
|
|
126
|
+
title="Convert API Specification to QType Tools",
|
|
127
|
+
description=(
|
|
128
|
+
"Converts an OpenAPI specification (URL or file path) to QType tool definitions. "
|
|
129
|
+
"Returns YAML code containing the generated tools, custom types, and authentication "
|
|
130
|
+
"providers that can be used in QType applications."
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
async def convert_api_to_tools(api_spec: str) -> str:
|
|
134
|
+
"""Convert API specification to QType YAML format.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
api_spec: URL or file path to an OpenAPI/Swagger specification.
|
|
138
|
+
Examples: "https://api.example.com/openapi.json",
|
|
139
|
+
"/path/to/openapi.yaml"
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
YAML string containing the generated QType tools, types, and
|
|
143
|
+
authentication providers.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
Exception: If conversion fails or no tools are found.
|
|
147
|
+
"""
|
|
148
|
+
from qtype.application.converters.tools_from_api import tools_from_api
|
|
149
|
+
from qtype.dsl.model import Application, ToolList
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
api_name, auths, tools, types = tools_from_api(api_spec)
|
|
153
|
+
if not tools:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
f"No tools found from the API specification: {api_spec}"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Create document with or without Application wrapper
|
|
159
|
+
if not auths and not types:
|
|
160
|
+
doc = ToolList(root=list(tools))
|
|
161
|
+
else:
|
|
162
|
+
doc = Application(
|
|
163
|
+
id=api_name,
|
|
164
|
+
description=(
|
|
165
|
+
f"Tools created from API specification {api_spec}"
|
|
166
|
+
),
|
|
167
|
+
tools=list(tools),
|
|
168
|
+
types=types,
|
|
169
|
+
auths=auths,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return convert_to_yaml(doc)
|
|
173
|
+
|
|
174
|
+
except Exception as e:
|
|
175
|
+
raise Exception(f"API conversion failed: {str(e)}")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@mcp.tool(
|
|
179
|
+
title="Convert Python Module to QType Tools",
|
|
180
|
+
description=(
|
|
181
|
+
"Converts Python functions from a module to QType tool definitions. "
|
|
182
|
+
"Analyzes function signatures, docstrings, and type hints to generate "
|
|
183
|
+
"YAML tool definitions that can be used in QType applications."
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
async def convert_python_to_tools(module_path: str) -> str:
|
|
187
|
+
"""Convert Python module to QType YAML format.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
module_path: Path to the Python module to convert.
|
|
191
|
+
Example: "my_package.my_module" or "/path/to/module.py"
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
YAML string containing the generated QType tools and custom types.
|
|
195
|
+
|
|
196
|
+
Raises:
|
|
197
|
+
Exception: If conversion fails or no tools are found.
|
|
198
|
+
"""
|
|
199
|
+
from qtype.application.converters.tools_from_module import (
|
|
200
|
+
tools_from_module,
|
|
201
|
+
)
|
|
202
|
+
from qtype.dsl.model import Application, ToolList
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
tools, types = tools_from_module(module_path)
|
|
206
|
+
if not tools:
|
|
207
|
+
raise ValueError(f"No tools found in the module: {module_path}")
|
|
208
|
+
|
|
209
|
+
# Create document with or without Application wrapper
|
|
210
|
+
if types:
|
|
211
|
+
doc = Application(
|
|
212
|
+
id=module_path,
|
|
213
|
+
description=(
|
|
214
|
+
f"Tools created from Python module {module_path}"
|
|
215
|
+
),
|
|
216
|
+
tools=list(tools),
|
|
217
|
+
types=types,
|
|
218
|
+
)
|
|
219
|
+
else:
|
|
220
|
+
doc = ToolList(root=list(tools))
|
|
221
|
+
|
|
222
|
+
return convert_to_yaml(doc)
|
|
223
|
+
|
|
224
|
+
except Exception as e:
|
|
225
|
+
raise Exception(f"Python module conversion failed: {str(e)}")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@mcp.tool(
|
|
229
|
+
title="Get QType Component Schema",
|
|
230
|
+
description=(
|
|
231
|
+
"Returns the JSON Schema definition for a specific QType component. "
|
|
232
|
+
"Use this to understand the structure, required fields, and allowed values "
|
|
233
|
+
"when building QType YAML specifications. "
|
|
234
|
+
"Common components include: Flow, Agent, LLMInference, DocumentSource, "
|
|
235
|
+
"Application, Model, Variable, CustomType. "
|
|
236
|
+
"Component names are case-sensitive and must match exactly."
|
|
237
|
+
),
|
|
238
|
+
structured_output=True,
|
|
239
|
+
)
|
|
240
|
+
def get_component_schema(component_name: str) -> dict[str, Any]:
|
|
241
|
+
"""Get the JSON Schema definition for a QType component.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
component_name: The exact name of the QType component (case-sensitive).
|
|
245
|
+
Examples: "DocumentSource", "Flow", "Agent", "LLMInference",
|
|
246
|
+
"Application", "Model", "Variable", "CustomType".
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
JSON Schema definition for the component including its properties,
|
|
250
|
+
required fields, types, and descriptions.
|
|
251
|
+
|
|
252
|
+
Raises:
|
|
253
|
+
ValueError: If component_name is not found. The error message will
|
|
254
|
+
include a list of all available component names.
|
|
255
|
+
"""
|
|
256
|
+
schema = _load_schema()
|
|
257
|
+
|
|
258
|
+
# Look up the component in $defs
|
|
259
|
+
if "$defs" not in schema:
|
|
260
|
+
raise ValueError("Schema file does not contain $defs section")
|
|
261
|
+
|
|
262
|
+
if component_name not in schema["$defs"]:
|
|
263
|
+
available = sorted(schema["$defs"].keys())
|
|
264
|
+
raise ValueError(
|
|
265
|
+
f"Component '{component_name}' not found in schema. "
|
|
266
|
+
f"Available components: {', '.join(available)}"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return schema["$defs"][component_name]
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@mcp.tool(
|
|
273
|
+
title="Get QType Documentation",
|
|
274
|
+
description=(
|
|
275
|
+
"Returns the content of a specific documentation file. "
|
|
276
|
+
"Use list_documentation first to see available files. "
|
|
277
|
+
"Provide the relative path (e.g., 'components/Flow.md', 'index.md', "
|
|
278
|
+
"'Tutorials/getting_started.md')."
|
|
279
|
+
),
|
|
280
|
+
)
|
|
281
|
+
def get_documentation(file_path: str) -> str:
|
|
282
|
+
"""Get the content of a specific documentation file.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
file_path: Relative path to the documentation file from the docs root.
|
|
286
|
+
Example: "components/Flow.md", "index.md", "Tutorials/getting_started.md".
|
|
287
|
+
Use list_documentation to see all available files.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
The full markdown content of the documentation file.
|
|
291
|
+
|
|
292
|
+
Raises:
|
|
293
|
+
FileNotFoundError: If the specified file doesn't exist.
|
|
294
|
+
ValueError: If the path tries to access files outside the docs directory.
|
|
295
|
+
"""
|
|
296
|
+
docs_path = _get_docs_path()
|
|
297
|
+
|
|
298
|
+
# Resolve the requested file path
|
|
299
|
+
requested_file = (docs_path / file_path).resolve()
|
|
300
|
+
|
|
301
|
+
# Security check: ensure the resolved path is within docs directory
|
|
302
|
+
try:
|
|
303
|
+
requested_file.relative_to(docs_path.resolve())
|
|
304
|
+
except ValueError:
|
|
305
|
+
raise ValueError(
|
|
306
|
+
f"Invalid path: '{file_path}' is outside documentation directory"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
if not requested_file.exists():
|
|
310
|
+
raise FileNotFoundError(
|
|
311
|
+
f"Documentation file not found: '{file_path}'. "
|
|
312
|
+
"Use list_documentation to see available files."
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
if not requested_file.is_file():
|
|
316
|
+
raise ValueError(f"Path is not a file: '{file_path}'")
|
|
317
|
+
|
|
318
|
+
content = requested_file.read_text(encoding="utf-8")
|
|
319
|
+
return resolve_snippets(content, requested_file)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@mcp.tool(
|
|
323
|
+
title="List QType Components",
|
|
324
|
+
description=(
|
|
325
|
+
"Returns a list of all available QType component types that can be used "
|
|
326
|
+
"in YAML specifications. Use this to discover what components exist before "
|
|
327
|
+
"requesting their detailed schemas with get_component_schema."
|
|
328
|
+
),
|
|
329
|
+
structured_output=True,
|
|
330
|
+
)
|
|
331
|
+
def list_components() -> list[str]:
|
|
332
|
+
"""List all available QType component types.
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Sorted list of all component names available in the QType schema.
|
|
336
|
+
Each name can be used with get_component_schema to retrieve its
|
|
337
|
+
full JSON Schema definition.
|
|
338
|
+
"""
|
|
339
|
+
schema = _load_schema()
|
|
340
|
+
|
|
341
|
+
if "$defs" not in schema:
|
|
342
|
+
raise ValueError("Schema file does not contain $defs section")
|
|
343
|
+
|
|
344
|
+
return sorted(schema["$defs"].keys())
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
@mcp.tool(
|
|
348
|
+
title="List QType Documentation",
|
|
349
|
+
description=(
|
|
350
|
+
"Returns a list of all available documentation files. "
|
|
351
|
+
"Use this to discover what documentation exists, then retrieve "
|
|
352
|
+
"specific files with get_documentation. Files are organized by category: "
|
|
353
|
+
"components/ (component reference), Concepts/ (conceptual guides), "
|
|
354
|
+
"Tutorials/ (step-by-step tutorials), How To/ (task guides), etc."
|
|
355
|
+
),
|
|
356
|
+
structured_output=True,
|
|
357
|
+
)
|
|
358
|
+
def list_documentation() -> list[str]:
|
|
359
|
+
"""List all available documentation markdown files.
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
Sorted list of relative paths to all .md documentation files.
|
|
363
|
+
Paths are relative to the docs root (e.g., "components/Flow.md",
|
|
364
|
+
"Tutorials/getting_started.md").
|
|
365
|
+
"""
|
|
366
|
+
docs_path = _get_docs_path()
|
|
367
|
+
|
|
368
|
+
if not docs_path.exists():
|
|
369
|
+
raise FileNotFoundError(
|
|
370
|
+
f"Documentation directory not found: {docs_path}"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Find all markdown files
|
|
374
|
+
md_files = []
|
|
375
|
+
for md_file in docs_path.rglob("*.md"):
|
|
376
|
+
# Get relative path from docs root
|
|
377
|
+
rel_path = md_file.relative_to(docs_path)
|
|
378
|
+
md_files.append(str(rel_path))
|
|
379
|
+
|
|
380
|
+
return sorted(md_files)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
@mcp.tool(
|
|
384
|
+
title="Validate QType YAML",
|
|
385
|
+
description=(
|
|
386
|
+
"Validates QType YAML for syntax and semantic correctness. "
|
|
387
|
+
"Returns a human-readable status string: either a success message or "
|
|
388
|
+
"a validation error with details."
|
|
389
|
+
),
|
|
390
|
+
)
|
|
391
|
+
async def validate_qtype_yaml(yaml_content: str) -> str:
|
|
392
|
+
"""Validate QType YAML for syntax and semantic errors.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
yaml_content: The QType YAML content to validate.
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
A human-readable status string.
|
|
399
|
+
"""
|
|
400
|
+
from qtype.semantic.loader import load_from_string
|
|
401
|
+
|
|
402
|
+
try:
|
|
403
|
+
document, _ = load_from_string(yaml_content)
|
|
404
|
+
return "✅ Valid QType Code"
|
|
405
|
+
|
|
406
|
+
except Exception as e:
|
|
407
|
+
# Return the error message so the LLM can fix it
|
|
408
|
+
return f"❌ Validation Error: {str(e)}"
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
@mcp.tool(
|
|
412
|
+
title="Visualize QType Architecture",
|
|
413
|
+
description=(
|
|
414
|
+
"Generates a Mermaid flowchart diagram from QType YAML code. "
|
|
415
|
+
"Returns a structured result containing raw Mermaid code plus preview guidance. "
|
|
416
|
+
"After calling this tool, call the mermaid-diagram-preview tool using the "
|
|
417
|
+
"returned mermaid_diagram_preview_input."
|
|
418
|
+
),
|
|
419
|
+
structured_output=True,
|
|
420
|
+
)
|
|
421
|
+
async def visualize_qtype_architecture(
|
|
422
|
+
yaml_content: str,
|
|
423
|
+
) -> MermaidVisualizationResult:
|
|
424
|
+
"""Generate Mermaid diagram from QType YAML.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
yaml_content: The complete QType YAML specification to visualize.
|
|
428
|
+
Must be a valid Application definition.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
A structured result with:
|
|
432
|
+
- mermaid_code: Raw Mermaid diagram code (no backticks/fences)
|
|
433
|
+
- suggested_next_tool: "mermaid-diagram-preview"
|
|
434
|
+
- preview_instructions: How to preview in VS Code
|
|
435
|
+
|
|
436
|
+
Raises:
|
|
437
|
+
Exception: If YAML is invalid or visualization fails.
|
|
438
|
+
"""
|
|
439
|
+
from qtype.semantic.loader import load_from_string
|
|
440
|
+
from qtype.semantic.model import Application
|
|
441
|
+
from qtype.semantic.visualize import visualize_application
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
document, _ = load_from_string(yaml_content)
|
|
445
|
+
if not isinstance(document, Application):
|
|
446
|
+
raise ValueError(
|
|
447
|
+
"YAML must contain an Application to visualize. "
|
|
448
|
+
f"Got {type(document).__name__} instead."
|
|
449
|
+
)
|
|
450
|
+
mermaid_content = visualize_application(document)
|
|
451
|
+
|
|
452
|
+
return MermaidVisualizationResult(
|
|
453
|
+
mermaid_code=mermaid_content,
|
|
454
|
+
mermaid_markdown=f"```mermaid\n{mermaid_content}\n```\n",
|
|
455
|
+
suggested_next_tool="mermaid-diagram-preview",
|
|
456
|
+
mermaid_diagram_preview_input=MermaidDiagramPreviewInput(
|
|
457
|
+
code=mermaid_content
|
|
458
|
+
),
|
|
459
|
+
preview_instructions=(
|
|
460
|
+
"Call mermaid-diagram-preview with mermaid_diagram_preview_input. "
|
|
461
|
+
"Alternatively, save mermaid_code in a .md file fenced with "
|
|
462
|
+
"```mermaid ...``` and open the Markdown preview."
|
|
463
|
+
),
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
except Exception as e:
|
|
467
|
+
raise RuntimeError(f"Visualization failed: {e}") from e
|
qtype/semantic/checker.py
CHANGED
|
@@ -704,7 +704,7 @@ def check(model: BaseModel) -> None:
|
|
|
704
704
|
# Check if this model type has a validator
|
|
705
705
|
model_type = type(model)
|
|
706
706
|
if model_type in _VALIDATORS:
|
|
707
|
-
_VALIDATORS[model_type](model)
|
|
707
|
+
_VALIDATORS[model_type](model) # type: ignore[arg-type]
|
|
708
708
|
|
|
709
709
|
# Recursively validate all fields
|
|
710
710
|
for field_name, field_value in model:
|
qtype/semantic/generate.py
CHANGED
|
@@ -73,7 +73,7 @@ def sort_classes_by_inheritance(
|
|
|
73
73
|
):
|
|
74
74
|
graph.add_edge(base.__name__, class_name)
|
|
75
75
|
|
|
76
|
-
sorted_names = list(nx.topological_sort(graph))
|
|
76
|
+
sorted_names = list(nx.topological_sort(graph)) # type: ignore[arg-type]
|
|
77
77
|
|
|
78
78
|
# sorted_names = sorted(graph.nodes, key=lambda node: depths[node])
|
|
79
79
|
return [(name, class_dict[name]) for name in sorted_names]
|
|
@@ -490,10 +490,10 @@ def generate_semantic_class(class_name: str, cls: type) -> str:
|
|
|
490
490
|
# Only process fields that are actually defined on this class
|
|
491
491
|
for field_name in cls.__annotations__:
|
|
492
492
|
if (
|
|
493
|
-
field_name in cls.model_fields
|
|
493
|
+
field_name in cls.model_fields # type: ignore[operator]
|
|
494
494
|
and f"{class_name}.{field_name}" not in FIELDS_TO_IGNORE
|
|
495
495
|
):
|
|
496
|
-
field_info = cls.model_fields[field_name]
|
|
496
|
+
field_info = cls.model_fields[field_name] # type: ignore[index]
|
|
497
497
|
field_type = field_info.annotation
|
|
498
498
|
field_default = field_info.default
|
|
499
499
|
field_default_factory = field_info.default_factory
|