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.
Files changed (263) hide show
  1. qtype/` +0 -0
  2. qtype/application/__init__.py +0 -2
  3. qtype/application/converters/tools_from_api.py +67 -57
  4. qtype/application/converters/tools_from_module.py +66 -32
  5. qtype/base/types.py +6 -1
  6. qtype/commands/convert.py +3 -6
  7. qtype/commands/generate.py +97 -10
  8. qtype/commands/mcp.py +68 -0
  9. qtype/commands/run.py +116 -44
  10. qtype/commands/validate.py +4 -4
  11. qtype/docs/.pages +8 -0
  12. qtype/docs/Concepts/mental-model-and-philosophy.md +363 -0
  13. qtype/docs/Contributing/.pages +4 -0
  14. qtype/docs/Contributing/index.md +283 -0
  15. qtype/docs/Contributing/roadmap.md +81 -0
  16. qtype/docs/Decisions/ADR-001-Chat-vs-Completion-Endpoint-Features.md +56 -0
  17. qtype/docs/Gallery/dataflow_pipelines.md +81 -0
  18. qtype/docs/Gallery/dataflow_pipelines.mermaid +45 -0
  19. qtype/docs/Gallery/research_assistant.md +97 -0
  20. qtype/docs/Gallery/research_assistant.mermaid +42 -0
  21. qtype/docs/Gallery/simple_chatbot.md +38 -0
  22. qtype/docs/Gallery/simple_chatbot.mermaid +35 -0
  23. qtype/docs/How To/Authentication/configure_aws_authentication.md +60 -0
  24. qtype/docs/How To/Authentication/use_api_key_authentication.md +40 -0
  25. qtype/docs/How To/Command Line Usage/load_multiple_inputs_from_files.md +77 -0
  26. qtype/docs/How To/Command Line Usage/pass_inputs_on_the_cli.md +52 -0
  27. qtype/docs/How To/Command Line Usage/serve_with_auto_reload.md +27 -0
  28. qtype/docs/How To/Data Processing/adjust_concurrency.md +40 -0
  29. qtype/docs/How To/Data Processing/cache_step_results.md +71 -0
  30. qtype/docs/How To/Data Processing/decode_json_xml.md +24 -0
  31. qtype/docs/How To/Data Processing/explode_collections.md +40 -0
  32. qtype/docs/How To/Data Processing/gather_results.md +68 -0
  33. qtype/docs/How To/Data Processing/invoke_other_flows.md +71 -0
  34. qtype/docs/How To/Data Processing/load_data_from_athena.md +49 -0
  35. qtype/docs/How To/Data Processing/read_data_from_files.md +61 -0
  36. qtype/docs/How To/Data Processing/read_sql_databases.md +46 -0
  37. qtype/docs/How To/Data Processing/write_data_to_file.md +39 -0
  38. qtype/docs/How To/Invoke Models/call_large_language_models.md +51 -0
  39. qtype/docs/How To/Invoke Models/create_embeddings.md +49 -0
  40. qtype/docs/How To/Invoke Models/reuse_prompts_with_templates.md +38 -0
  41. qtype/docs/How To/Language Features/include_qtype_yaml.md +45 -0
  42. qtype/docs/How To/Language Features/include_raw_text_from_other_files.md +48 -0
  43. qtype/docs/How To/Language Features/reference_entities_by_id.md +51 -0
  44. qtype/docs/How To/Language Features/use_agent_skills.md +29 -0
  45. qtype/docs/How To/Language Features/use_environment_variables.md +48 -0
  46. qtype/docs/How To/Language Features/use_optional_variables.md +42 -0
  47. qtype/docs/How To/Language Features/use_qtype_mcp.md +59 -0
  48. qtype/docs/How To/Observability & Debugging/trace_calls_with_open_telemetry.md +49 -0
  49. qtype/docs/How To/Observability & Debugging/validate_qtype_yaml.md +36 -0
  50. qtype/docs/How To/Observability & Debugging/visualize_application_architecture.md +61 -0
  51. qtype/docs/How To/Observability & Debugging/visualize_example.mermaid +35 -0
  52. qtype/docs/How To/Qtype Server/flow_as_ui.png +0 -0
  53. qtype/docs/How To/Qtype Server/serve_flows_as_apis.md +40 -0
  54. qtype/docs/How To/Qtype Server/serve_flows_as_ui.md +41 -0
  55. qtype/docs/How To/Qtype Server/use_conversational_interfaces.md +56 -0
  56. qtype/docs/How To/Qtype Server/use_variables_with_ui_hints.md +48 -0
  57. qtype/docs/How To/Tools & Integration/bind_tool_inputs_and_outputs.md +47 -0
  58. qtype/docs/How To/Tools & Integration/create_tools_from_openapi_specifications.md +85 -0
  59. qtype/docs/How To/Tools & Integration/create_tools_from_python_modules.md +87 -0
  60. qtype/docs/Reference/cli.md +336 -0
  61. qtype/docs/Reference/plugins.md +99 -0
  62. qtype/docs/Reference/semantic-validation-rules.md +184 -0
  63. qtype/docs/Tutorials/.pages +1 -0
  64. qtype/docs/Tutorials/01-first-qtype-application.md +249 -0
  65. qtype/docs/Tutorials/02-conversational-chatbot.md +327 -0
  66. qtype/docs/Tutorials/03-structured-data.md +480 -0
  67. qtype/docs/Tutorials/04-tools-and-function-calling.md +476 -0
  68. qtype/docs/Tutorials/example_chat.png +0 -0
  69. qtype/docs/Tutorials/index.md +92 -0
  70. qtype/docs/components/APIKeyAuthProvider.md +7 -0
  71. qtype/docs/components/APITool.md +10 -0
  72. qtype/docs/components/AWSAuthProvider.md +13 -0
  73. qtype/docs/components/AWSSecretManager.md +5 -0
  74. qtype/docs/components/Agent.md +6 -0
  75. qtype/docs/components/Aggregate.md +7 -0
  76. qtype/docs/components/AggregateStats.md +7 -0
  77. qtype/docs/components/Application.md +22 -0
  78. qtype/docs/components/AuthorizationProvider.md +6 -0
  79. qtype/docs/components/AuthorizationProviderList.md +5 -0
  80. qtype/docs/components/BearerTokenAuthProvider.md +6 -0
  81. qtype/docs/components/BedrockReranker.md +8 -0
  82. qtype/docs/components/ChatContent.md +7 -0
  83. qtype/docs/components/ChatMessage.md +6 -0
  84. qtype/docs/components/Collect.md +6 -0
  85. qtype/docs/components/ConstantPath.md +5 -0
  86. qtype/docs/components/Construct.md +6 -0
  87. qtype/docs/components/CustomType.md +7 -0
  88. qtype/docs/components/Decoder.md +8 -0
  89. qtype/docs/components/DecoderFormat.md +8 -0
  90. qtype/docs/components/DocToTextConverter.md +7 -0
  91. qtype/docs/components/Document.md +7 -0
  92. qtype/docs/components/DocumentEmbedder.md +6 -0
  93. qtype/docs/components/DocumentIndex.md +7 -0
  94. qtype/docs/components/DocumentSearch.md +7 -0
  95. qtype/docs/components/DocumentSource.md +12 -0
  96. qtype/docs/components/DocumentSplitter.md +9 -0
  97. qtype/docs/components/Echo.md +8 -0
  98. qtype/docs/components/Embedding.md +7 -0
  99. qtype/docs/components/EmbeddingModel.md +6 -0
  100. qtype/docs/components/Explode.md +5 -0
  101. qtype/docs/components/FieldExtractor.md +21 -0
  102. qtype/docs/components/FileSource.md +6 -0
  103. qtype/docs/components/FileWriter.md +7 -0
  104. qtype/docs/components/Flow.md +14 -0
  105. qtype/docs/components/FlowInterface.md +7 -0
  106. qtype/docs/components/Index.md +8 -0
  107. qtype/docs/components/IndexUpsert.md +6 -0
  108. qtype/docs/components/InvokeEmbedding.md +7 -0
  109. qtype/docs/components/InvokeFlow.md +8 -0
  110. qtype/docs/components/InvokeTool.md +8 -0
  111. qtype/docs/components/LLMInference.md +9 -0
  112. qtype/docs/components/ListType.md +5 -0
  113. qtype/docs/components/Memory.md +8 -0
  114. qtype/docs/components/MessageRole.md +14 -0
  115. qtype/docs/components/Model.md +10 -0
  116. qtype/docs/components/ModelList.md +5 -0
  117. qtype/docs/components/OAuth2AuthProvider.md +9 -0
  118. qtype/docs/components/PrimitiveTypeEnum.md +20 -0
  119. qtype/docs/components/PromptTemplate.md +7 -0
  120. qtype/docs/components/PythonFunctionTool.md +7 -0
  121. qtype/docs/components/RAGChunk.md +7 -0
  122. qtype/docs/components/RAGDocument.md +10 -0
  123. qtype/docs/components/RAGSearchResult.md +8 -0
  124. qtype/docs/components/Reranker.md +5 -0
  125. qtype/docs/components/SQLSource.md +8 -0
  126. qtype/docs/components/Search.md +7 -0
  127. qtype/docs/components/SearchResult.md +7 -0
  128. qtype/docs/components/SecretManager.md +7 -0
  129. qtype/docs/components/SecretReference.md +7 -0
  130. qtype/docs/components/Source.md +5 -0
  131. qtype/docs/components/Step.md +8 -0
  132. qtype/docs/components/TelemetrySink.md +9 -0
  133. qtype/docs/components/Tool.md +9 -0
  134. qtype/docs/components/ToolList.md +5 -0
  135. qtype/docs/components/TypeList.md +5 -0
  136. qtype/docs/components/Variable.md +8 -0
  137. qtype/docs/components/VariableList.md +5 -0
  138. qtype/docs/components/VectorIndex.md +7 -0
  139. qtype/docs/components/VectorSearch.md +6 -0
  140. qtype/docs/components/VertexAuthProvider.md +9 -0
  141. qtype/docs/components/Writer.md +5 -0
  142. qtype/docs/example_ui.png +0 -0
  143. qtype/docs/index.md +81 -0
  144. qtype/docs/legacy_how_tos/.pages +6 -0
  145. qtype/docs/legacy_how_tos/Configuration/modular-yaml.md +366 -0
  146. qtype/docs/legacy_how_tos/Configuration/phoenix_projects.png +0 -0
  147. qtype/docs/legacy_how_tos/Configuration/phoenix_traces.png +0 -0
  148. qtype/docs/legacy_how_tos/Configuration/reference-by-id.md +251 -0
  149. qtype/docs/legacy_how_tos/Configuration/telemetry-setup.md +259 -0
  150. qtype/docs/legacy_how_tos/Data Types/custom-types.md +52 -0
  151. qtype/docs/legacy_how_tos/Data Types/domain-types.md +113 -0
  152. qtype/docs/legacy_how_tos/Debugging/visualize-apps.md +147 -0
  153. qtype/docs/legacy_how_tos/Tools/api-tools.md +29 -0
  154. qtype/docs/legacy_how_tos/Tools/python-tools.md +299 -0
  155. qtype/docs/skills/architect/SKILL.md +188 -0
  156. qtype/docs/skills/architect/references/cheatsheet.md +198 -0
  157. qtype/docs/skills/architect/references/patterns.md +29 -0
  158. qtype/docs/stylesheets/extra.css +27 -0
  159. qtype/dsl/custom_types.py +2 -1
  160. qtype/dsl/linker.py +23 -7
  161. qtype/dsl/loader.py +3 -3
  162. qtype/dsl/model.py +181 -67
  163. qtype/examples/authentication/aws_authentication.qtype.yaml +63 -0
  164. qtype/examples/conversational_ai/hello_world_chat.qtype.yaml +43 -0
  165. qtype/examples/conversational_ai/simple_chatbot.qtype.yaml +40 -0
  166. qtype/examples/data_processing/athena_query.qtype.yaml +56 -0
  167. qtype/examples/data_processing/batch_inputs.csv +5 -0
  168. qtype/examples/data_processing/batch_processing.qtype.yaml +54 -0
  169. qtype/examples/data_processing/cache_step_results.qtype.yaml +78 -0
  170. qtype/examples/data_processing/collect_results.qtype.yaml +55 -0
  171. qtype/examples/data_processing/create_sample_db.py +129 -0
  172. qtype/examples/data_processing/dataflow_pipelines.qtype.yaml +108 -0
  173. qtype/examples/data_processing/decode_json.qtype.yaml +23 -0
  174. qtype/examples/data_processing/explode_items.qtype.yaml +25 -0
  175. qtype/examples/data_processing/invoke_other_flows.qtype.yaml +98 -0
  176. qtype/examples/data_processing/read_file.qtype.yaml +60 -0
  177. qtype/examples/data_processing/reviews.db +0 -0
  178. qtype/examples/data_processing/sample_article.txt +1 -0
  179. qtype/examples/data_processing/sample_documents.jsonl +5 -0
  180. qtype/examples/invoke_models/create_embeddings.qtype.yaml +28 -0
  181. qtype/examples/invoke_models/simple_llm_call.qtype.yaml +32 -0
  182. qtype/examples/language_features/include_raw.qtype.yaml +27 -0
  183. qtype/examples/language_features/optional_variables.qtype.yaml +32 -0
  184. qtype/examples/language_features/story_prompt.txt +6 -0
  185. qtype/examples/language_features/ui_hints.qtype.yaml +52 -0
  186. qtype/examples/legacy/bedrock/data_analysis_with_telemetry.qtype.yaml +169 -0
  187. qtype/examples/legacy/bedrock/hello_world.qtype.yaml +39 -0
  188. qtype/examples/legacy/bedrock/hello_world_chat.qtype.yaml +37 -0
  189. qtype/examples/legacy/bedrock/hello_world_chat_with_telemetry.qtype.yaml +40 -0
  190. qtype/examples/legacy/bedrock/hello_world_chat_with_thinking.qtype.yaml +40 -0
  191. qtype/examples/legacy/bedrock/hello_world_completion.qtype.yaml +41 -0
  192. qtype/examples/legacy/bedrock/hello_world_completion_with_auth.qtype.yaml +44 -0
  193. qtype/examples/legacy/bedrock/simple_agent_chat.qtype.yaml +46 -0
  194. qtype/examples/legacy/chat_with_langfuse.qtype.yaml +50 -0
  195. qtype/examples/legacy/data/customers.csv +6 -0
  196. qtype/examples/legacy/data_processor.qtype.yaml +48 -0
  197. qtype/examples/legacy/echo/debug_example.qtype.yaml +59 -0
  198. qtype/examples/legacy/echo/prompt.qtype.yaml +22 -0
  199. qtype/examples/legacy/echo/readme.md +29 -0
  200. qtype/examples/legacy/echo/test.qtype.yaml +26 -0
  201. qtype/examples/legacy/echo/video.qtype.yaml +20 -0
  202. qtype/examples/legacy/field_extractor_example.qtype.yaml +137 -0
  203. qtype/examples/legacy/multi_flow_example.qtype.yaml +125 -0
  204. qtype/examples/legacy/openai/hello_world_chat.qtype.yaml +43 -0
  205. qtype/examples/legacy/openai/hello_world_chat_with_telemetry.qtype.yaml +46 -0
  206. qtype/examples/legacy/qtype_plugin_example.py +51 -0
  207. qtype/examples/legacy/rag.qtype.yaml +207 -0
  208. qtype/examples/legacy/sample_data.txt +43 -0
  209. qtype/examples/legacy/time_utilities.qtype.yaml +64 -0
  210. qtype/examples/legacy/vertex/README.md +11 -0
  211. qtype/examples/legacy/vertex/hello_world_chat.qtype.yaml +36 -0
  212. qtype/examples/legacy/vertex/hello_world_completion.qtype.yaml +40 -0
  213. qtype/examples/legacy/vertex/hello_world_completion_with_auth.qtype.yaml +45 -0
  214. qtype/examples/observability_debugging/trace_with_opentelemetry.qtype.yaml +40 -0
  215. qtype/examples/research_assistant/research_assistant.qtype.yaml +94 -0
  216. qtype/examples/research_assistant/tavily.oas.yaml +722 -0
  217. qtype/examples/research_assistant/tavily.qtype.yaml +216 -0
  218. qtype/examples/tutorials/01_hello_world.qtype.yaml +48 -0
  219. qtype/examples/tutorials/02_conversational_chat.qtype.yaml +37 -0
  220. qtype/examples/tutorials/03_structured_data.qtype.yaml +130 -0
  221. qtype/examples/tutorials/04_tools_and_function_calling.qtype.yaml +89 -0
  222. qtype/interpreter/api.py +4 -1
  223. qtype/interpreter/base/base_step_executor.py +3 -1
  224. qtype/interpreter/base/stream_emitter.py +19 -13
  225. qtype/interpreter/conversions.py +7 -3
  226. qtype/interpreter/converters.py +142 -26
  227. qtype/interpreter/executors/agent_executor.py +2 -3
  228. qtype/interpreter/executors/aggregate_executor.py +3 -4
  229. qtype/interpreter/executors/construct_executor.py +15 -15
  230. qtype/interpreter/executors/doc_to_text_executor.py +1 -3
  231. qtype/interpreter/executors/field_extractor_executor.py +13 -12
  232. qtype/interpreter/executors/file_source_executor.py +21 -34
  233. qtype/interpreter/executors/file_writer_executor.py +4 -4
  234. qtype/interpreter/executors/index_upsert_executor.py +1 -1
  235. qtype/interpreter/executors/invoke_embedding_executor.py +1 -4
  236. qtype/interpreter/executors/invoke_flow_executor.py +2 -2
  237. qtype/interpreter/executors/invoke_tool_executor.py +19 -18
  238. qtype/interpreter/executors/llm_inference_executor.py +16 -18
  239. qtype/interpreter/executors/prompt_template_executor.py +1 -3
  240. qtype/interpreter/executors/sql_source_executor.py +1 -1
  241. qtype/interpreter/resource_cache.py +3 -1
  242. qtype/interpreter/rich_progress.py +6 -3
  243. qtype/interpreter/stream/chat/converter.py +25 -17
  244. qtype/interpreter/stream/chat/ui_request_to_domain_type.py +2 -2
  245. qtype/interpreter/tools/function_tool_helper.py +11 -10
  246. qtype/interpreter/types.py +89 -4
  247. qtype/interpreter/typing.py +35 -38
  248. qtype/mcp/__init__.py +0 -0
  249. qtype/mcp/server.py +722 -0
  250. qtype/schema/qtype.schema.json +4016 -0
  251. qtype/semantic/checker.py +20 -1
  252. qtype/semantic/generate.py +6 -9
  253. qtype/semantic/model.py +26 -33
  254. qtype/semantic/resolver.py +7 -0
  255. qtype/semantic/visualize.py +45 -53
  256. {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/METADATA +65 -44
  257. qtype-0.1.13.dist-info/RECORD +352 -0
  258. {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/WHEEL +1 -2
  259. qtype/application/facade.py +0 -177
  260. qtype-0.1.11.dist-info/RECORD +0 -142
  261. qtype-0.1.11.dist-info/top_level.txt +0 -1
  262. {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/entry_points.txt +0 -0
  263. {qtype-0.1.11.dist-info → qtype-0.1.13.dist-info}/licenses/LICENSE +0 -0
qtype/semantic/checker.py CHANGED
@@ -50,6 +50,10 @@ class FlowHasNoStepsError(QTypeValidationError):
50
50
  # Alias for backward compatibility and semantic clarity
51
51
  QTypeSemanticError = SemanticError
52
52
 
53
+ # Step types that support text streaming
54
+ # These are the only step types that can produce streaming text output
55
+ STREAMING_STEP_TYPES = (LLMInference, Agent)
56
+
53
57
 
54
58
  # ---- Helper Functions for Common Validation Patterns ----
55
59
 
@@ -543,6 +547,21 @@ def _validate_flow(flow: Flow) -> None:
543
547
  f"Flow {flow.id} has a Complete interface but {len(text_outputs)} text outputs -- there should be 1."
544
548
  )
545
549
 
550
+ # Ensure the final step supports streaming for Complete interface
551
+ if flow.steps:
552
+ final_step = flow.steps[-1]
553
+ if not isinstance(final_step, STREAMING_STEP_TYPES):
554
+ streaming_type_names = ", ".join(
555
+ t.__name__ for t in STREAMING_STEP_TYPES
556
+ )
557
+ raise QTypeSemanticError(
558
+ (
559
+ f"Flow {flow.id} has a Complete interface which requires streaming output, "
560
+ f"but the final step '{final_step.id}' is of type '{final_step.type}' which does not support streaming. "
561
+ f"The final step must be one of: {streaming_type_names}."
562
+ )
563
+ )
564
+
546
565
 
547
566
  def _has_secret_reference(obj: Any) -> bool:
548
567
  """
@@ -704,7 +723,7 @@ def check(model: BaseModel) -> None:
704
723
  # Check if this model type has a validator
705
724
  model_type = type(model)
706
725
  if model_type in _VALIDATORS:
707
- _VALIDATORS[model_type](model)
726
+ _VALIDATORS[model_type](model) # type: ignore[arg-type]
708
727
 
709
728
  # Recursively validate all fields
710
729
  for field_name, field_value in model:
@@ -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]
@@ -144,8 +144,7 @@ def generate_semantic_model(args: argparse.Namespace) -> None:
144
144
  CustomType,
145
145
  DecoderFormat,
146
146
  ListType,
147
- PrimitiveTypeEnum,
148
- ToolParameter
147
+ PrimitiveTypeEnum
149
148
  )
150
149
  from qtype.dsl.model import Variable as DSLVariable # noqa: F401
151
150
  from qtype.dsl.model import VariableType # noqa: F401
@@ -158,10 +157,8 @@ def generate_semantic_model(args: argparse.Namespace) -> None:
158
157
  f.write(
159
158
  dedent('''
160
159
  class Variable(DSLVariable, BaseModel):
161
- """Semantic version of DSL Variable with ID references resolved."""
162
- value: Any | None = Field(None, description="The value of the variable")
163
- def is_set(self) -> bool:
164
- return self.value is not None
160
+ """Semantic version of DSL Variable."""
161
+ pass
165
162
 
166
163
  ''').lstrip()
167
164
  )
@@ -490,10 +487,10 @@ def generate_semantic_class(class_name: str, cls: type) -> str:
490
487
  # Only process fields that are actually defined on this class
491
488
  for field_name in cls.__annotations__:
492
489
  if (
493
- field_name in cls.model_fields
490
+ field_name in cls.model_fields # type: ignore[operator]
494
491
  and f"{class_name}.{field_name}" not in FIELDS_TO_IGNORE
495
492
  ):
496
- field_info = cls.model_fields[field_name]
493
+ field_info = cls.model_fields[field_name] # type: ignore[index]
497
494
  field_type = field_info.annotation
498
495
  field_default = field_info.default
499
496
  field_default_factory = field_info.default_factory
qtype/semantic/model.py CHANGED
@@ -32,19 +32,15 @@ from qtype.dsl.model import ( # noqa: F401
32
32
  DecoderFormat,
33
33
  ListType,
34
34
  PrimitiveTypeEnum,
35
- ToolParameter,
36
35
  )
37
36
  from qtype.dsl.model import Variable as DSLVariable # noqa: F401
38
37
  from qtype.semantic.base_types import ImmutableModel
39
38
 
40
39
 
41
40
  class Variable(DSLVariable, BaseModel):
42
- """Semantic version of DSL Variable with ID references resolved."""
41
+ """Semantic version of DSL Variable."""
43
42
 
44
- value: Any | None = Field(None, description="The value of the variable")
45
-
46
- def is_set(self) -> bool:
47
- return self.value is not None
43
+ pass
48
44
 
49
45
 
50
46
  class AuthorizationProvider(ImmutableModel):
@@ -66,12 +62,12 @@ class Tool(ImmutableModel):
66
62
  description: str = Field(
67
63
  ..., description="Description of what the tool does."
68
64
  )
69
- inputs: dict[str, ToolParameter] = Field(
70
- default_factory=dict,
65
+ inputs: list[Variable] = Field(
66
+ default_factory=list,
71
67
  description="Input parameters required by this tool.",
72
68
  )
73
- outputs: dict[str, ToolParameter] = Field(
74
- default_factory=dict,
69
+ outputs: list[Variable] = Field(
70
+ default_factory=list,
75
71
  description="Output parameters produced by this tool.",
76
72
  )
77
73
 
@@ -421,9 +417,9 @@ class APITool(Tool):
421
417
  default_factory=dict,
422
418
  description="Optional HTTP headers to include in the request.",
423
419
  )
424
- parameters: dict[str, ToolParameter] = Field(
425
- default_factory=dict,
426
- description="Output parameters produced by this tool.",
420
+ parameters: list[Variable] = Field(
421
+ default_factory=list,
422
+ description="Path and query parameters for the API call.",
427
423
  )
428
424
 
429
425
 
@@ -447,15 +443,12 @@ class AWSSecretManager(SecretManager):
447
443
 
448
444
  class Aggregate(Step):
449
445
  """
450
- A terminal step that consumes an entire input stream and produces a single
451
- summary message with success/error counts.
446
+ A step that, after all messages have been processed,
447
+ returns a single message containing the counts of successful and failed
448
+ messages. Other messages are passed through unchanged.
452
449
  """
453
450
 
454
451
  type: Literal["Aggregate"] = Field("Aggregate")
455
- outputs: list[Variable] = Field(
456
- default_factory=list,
457
- description="References to the variables for the output. There should be one and only one output with type AggregateStats",
458
- )
459
452
 
460
453
 
461
454
  class Collect(Step, BatchableStepMixin):
@@ -472,8 +465,9 @@ class Construct(Step):
472
465
  """A step that converts variables into an instance of a Custom or Domain Type"""
473
466
 
474
467
  type: Literal["Construct"] = Field("Construct")
475
- field_mapping: dict[str, str] = Field(
476
- ..., description="Mapping of type inputs to variable names, if needed."
468
+ field_bindings: dict[str, Variable] = Field(
469
+ ...,
470
+ description="Mapping from type field names to flow variable names.",
477
471
  )
478
472
 
479
473
 
@@ -558,6 +552,9 @@ class FieldExtractor(Step):
558
552
  The extracted data is used to construct the output variable by passing it
559
553
  as keyword arguments to the output type's constructor.
560
554
 
555
+ If there is no match and the output variable is optional, it is set to None.
556
+ If there is no match and the output variable is required, an error is raised.
557
+
561
558
  Example JSONPath expressions:
562
559
  - `$.field_name` - Extract a single field
563
560
  - `$.items[*]` - Extract all items from a list
@@ -569,10 +566,6 @@ class FieldExtractor(Step):
569
566
  ...,
570
567
  description="JSONPath expression to extract data from the input. Uses jsonpath-ng syntax.",
571
568
  )
572
- fail_on_missing: bool = Field(
573
- True,
574
- description="Whether to raise an error if the JSONPath matches no data. If False, returns None.",
575
- )
576
569
 
577
570
 
578
571
  class InvokeEmbedding(Step, ConcurrentStepMixin):
@@ -590,13 +583,13 @@ class InvokeFlow(Step):
590
583
 
591
584
  type: Literal["InvokeFlow"] = Field("InvokeFlow")
592
585
  flow: Flow = Field(..., description="Flow to invoke.")
593
- input_bindings: dict[Variable, str] = Field(
586
+ input_bindings: dict[str, Variable] = Field(
594
587
  ...,
595
- description="Mapping from variable references to flow input variable IDs.",
588
+ description="Mapping from flow input variable IDs to step variable names.",
596
589
  )
597
- output_bindings: dict[Variable, str] = Field(
590
+ output_bindings: dict[str, Variable] = Field(
598
591
  ...,
599
- description="Mapping from variable references to flow output variable IDs.",
592
+ description="Mapping from flow output variable IDs to step variable names.",
600
593
  )
601
594
 
602
595
 
@@ -605,13 +598,13 @@ class InvokeTool(Step, ConcurrentStepMixin):
605
598
 
606
599
  type: Literal["InvokeTool"] = Field("InvokeTool")
607
600
  tool: Tool = Field(..., description="Tool to invoke.")
608
- input_bindings: dict[str, str] = Field(
601
+ input_bindings: dict[str, Variable] = Field(
609
602
  ...,
610
- description="Mapping from variable references to tool input parameter names.",
603
+ description="Mapping from tool parameter names to flow variable names.",
611
604
  )
612
- output_bindings: dict[str, str] = Field(
605
+ output_bindings: dict[str, Variable] = Field(
613
606
  ...,
614
- description="Mapping from variable references to tool output parameter names.",
607
+ description="Mapping from tool output names to flow variable names.",
615
608
  )
616
609
 
617
610
 
@@ -71,6 +71,13 @@ def to_semantic_ir(
71
71
  # If the object is already in the symbol table, return it.
72
72
  return symbol_table[obj_id]
73
73
 
74
+ if isinstance(dslobj, dict):
75
+ # If the object is a dict, recursively resolve each value
76
+ return {
77
+ key: to_semantic_ir(value, symbol_table)
78
+ for key, value in dslobj.items()
79
+ }
80
+
74
81
  if isinstance(dslobj, list):
75
82
  # If the object is a list, we will resolve each item in the list.
76
83
  return [to_semantic_ir(item, symbol_table) for item in dslobj] # type: ignore
@@ -21,6 +21,8 @@ from qtype.semantic.model import (
21
21
  DocumentIndex,
22
22
  DocumentSearch,
23
23
  Flow,
24
+ InvokeFlow,
25
+ InvokeTool,
24
26
  LLMInference,
25
27
  Memory,
26
28
  Model,
@@ -47,7 +49,7 @@ def visualize_application(app: Application) -> str:
47
49
  """
48
50
  lines = [
49
51
  "flowchart TD",
50
- f' subgraph APP ["📱 Application: {app.id}"]',
52
+ f' subgraph APP ["📱 {app.id}"]',
51
53
  " direction TB",
52
54
  "",
53
55
  ]
@@ -117,8 +119,8 @@ def _generate_flow_subgraph(
117
119
  flow: Flow, flow_id: str
118
120
  ) -> tuple[list[str], list[str]]:
119
121
  """Generate a flow subgraph with internal nodes and return external connections."""
120
- # Add more spacing and line breaks for better SVG rendering
121
- description = f"\n{flow.description}" if flow.description else ""
122
+ # Keep labels concise - no multi-line descriptions in labels
123
+ flow_label = f"🔄 {flow.id}"
122
124
 
123
125
  # Choose direction based on flow characteristics:
124
126
  # - Flows with interface (e.g., Conversational) use LR (left-right)
@@ -126,7 +128,7 @@ def _generate_flow_subgraph(
126
128
  direction = "LR" if flow.interface else "TB"
127
129
 
128
130
  lines = [
129
- f' subgraph {flow_id} ["🔄 Flow: {flow.id}{description}"]',
131
+ f' subgraph {flow_id} ["{flow_label}"]',
130
132
  f" direction {direction}",
131
133
  ]
132
134
 
@@ -167,20 +169,31 @@ def _generate_step_node(
167
169
  lines = []
168
170
  external_connections = []
169
171
 
170
- if isinstance(step, Flow):
171
- # Nested flow
172
+ if isinstance(step, InvokeFlow):
172
173
  lines.append(
173
- f' {node_id}@{{shape: subproc, label: "📋 Flow: {step.id}"}}'
174
+ f' {node_id}@{{shape: sub-r, label: "🔄 {step.id}"}}'
175
+ )
176
+ # Connect to the invoked flow
177
+ invoked_flow_id = f"FLOW_{_sanitize_id(step.flow.id)}"
178
+ external_connections.append(
179
+ f" {node_id} -.->|invokes| {invoked_flow_id}"
174
180
  )
175
181
  elif isinstance(step, Agent):
176
182
  # Agent with tools
177
183
  lines.append(
178
- f' {node_id}@{{shape: hex, label: "🤖 Agent: {step.id}"}}'
184
+ f' {node_id}@{{shape: hex, label: "🤖 {step.id}"}}'
179
185
  )
180
186
  # Connect to tools
181
187
  for tool in step.tools:
182
188
  tool_id = f"TOOL_{_sanitize_id(tool.id)}"
183
- external_connections.append(f" {node_id} -.-> {tool_id}")
189
+ external_connections.append(f" {node_id} -.->|uses| {tool_id}")
190
+ elif isinstance(step, InvokeTool):
191
+ lines.append(
192
+ f' {node_id}@{{shape: rect, label: "⚙️ {step.id}"}}'
193
+ )
194
+
195
+ tool_id = f"TOOL_{_sanitize_id(step.tool.id)}"
196
+ external_connections.append(f" {node_id} -.->|uses| {tool_id}")
184
197
  elif isinstance(step, LLMInference):
185
198
  lines.append(
186
199
  f' {node_id}@{{shape: rounded, label: "✨ {step.id}"}}'
@@ -196,7 +209,7 @@ def _generate_step_node(
196
209
  )
197
210
  elif isinstance(step, PromptTemplate):
198
211
  lines.append(
199
- f' {node_id}@{{shape: doc, label: "📄 Template: {step.id}"}}'
212
+ f' {node_id}@{{shape: doc, label: "📄 {step.id}"}}'
200
213
  )
201
214
  elif isinstance(step, Decoder):
202
215
  format_label = (
@@ -205,46 +218,30 @@ def _generate_step_node(
205
218
  else str(step.format)
206
219
  )
207
220
  lines.append(
208
- f' {node_id}@{{shape: lean-r, label: "🔍 Decode: {step.id} ({format_label})"}}'
221
+ f' {node_id}@{{shape: lean-r, label: "🔍 {step.id} ({format_label})"}}'
209
222
  )
210
223
  elif isinstance(step, VectorSearch):
211
224
  lines.append(
212
- f' {node_id}@{{shape: cyl, label: "🔎 Vector Search: {step.id}"}}'
225
+ f' {node_id}@{{shape: cyl, label: "🔎 {step.id}"}}'
213
226
  )
214
227
  index_id = f"INDEX_{_sanitize_id(step.index.id)}"
215
228
  external_connections.append(f" {node_id} -.-> {index_id}")
216
229
  elif isinstance(step, DocumentSearch):
217
230
  lines.append(
218
- f' {node_id}@{{shape: cyl, label: "📚 Doc Search: {step.id}"}}'
231
+ f' {node_id}@{{shape: cyl, label: "📚 {step.id}"}}'
219
232
  )
220
233
  index_id = f"INDEX_{_sanitize_id(step.index.id)}"
221
234
  external_connections.append(f" {node_id} -.-> {index_id}")
222
235
  elif isinstance(step, Search):
223
236
  lines.append(
224
- f' {node_id}@{{shape: cyl, label: "🔍 Search: {step.id}"}}'
237
+ f' {node_id}@{{shape: cyl, label: "🔍 {step.id}"}}'
225
238
  )
226
239
  index_id = f"INDEX_{_sanitize_id(step.index.id)}"
227
240
  external_connections.append(f" {node_id} -.-> {index_id}")
228
- elif isinstance(step, APITool):
229
- method_label = step.method.upper()
230
- lines.append(
231
- f' {node_id}["⚡ API: {step.id} ({method_label})"]'
232
- )
233
- if step.auth:
234
- auth_id = f"AUTH_{_sanitize_id(step.auth.id)}"
235
- external_connections.append(f" {node_id} -.-> {auth_id}")
236
- elif isinstance(step, PythonFunctionTool):
237
- lines.append(
238
- f' {node_id}@{{shape: rect, label: "🐍 Python: {step.id} {step.function_name}"}}'
239
- )
240
- elif isinstance(step, Tool):
241
- lines.append(
242
- f' {node_id}@{{shape: rect, label: "🔧 Tool: {step.id}"}}'
243
- )
244
241
  else:
245
242
  # Generic step
246
243
  lines.append(
247
- f' {node_id}@{{shape: rect, label: "⚙️ Step: {step.id}"}}'
244
+ f' {node_id}@{{shape: rect, label: "⚙️ {step.id}"}}'
248
245
  )
249
246
 
250
247
  return lines, external_connections
@@ -276,20 +273,11 @@ def _generate_step_connections(
276
273
  # consumes and produces the same variable)
277
274
  if producer_id == node_id:
278
275
  continue
279
- # Get a simple string representation of the variable type
280
- var_type = str(input_var.type).split(".")[
281
- -1
282
- ] # Get the last part after dots
283
- var_id_and_type = f"{input_var.id}: {var_type}"
284
-
285
- is_list = str(var_type).startswith("list[") and str(
286
- var_type
287
- ).endswith("]")
288
- if is_list:
289
- var_id_and_type = f'"{var_id_and_type}"'
276
+ # Use simple variable name only - no type annotations
277
+ var_label = input_var.id
290
278
 
291
279
  lines.append(
292
- f" {producer_id} -->|{var_id_and_type}| {node_id}"
280
+ f" {producer_id} -->|{var_label}| {node_id}"
293
281
  )
294
282
 
295
283
  # Then, register this step's outputs for future steps
@@ -374,7 +362,7 @@ def _generate_shared_resources(app: Application) -> list[str]:
374
362
  auth_id = f"AUTH_{_sanitize_id(auth.id)}"
375
363
  auth_type = auth.type.upper()
376
364
  lines.append(
377
- f' {auth_id}@{{shape: hex, label: "🔐 {auth.id}\\n{auth_type}"}}'
365
+ f' {auth_id}@{{shape: hex, label: "🔐 {auth.id} ({auth_type})"}}'
378
366
  )
379
367
 
380
368
  # Models
@@ -394,7 +382,7 @@ def _generate_shared_resources(app: Application) -> list[str]:
394
382
  index_id = f"INDEX_{_sanitize_id(index.id)}"
395
383
  if isinstance(index, VectorIndex):
396
384
  lines.append(
397
- f' {index_id}@{{shape: cyl, label: "🗂️ Vector: {index.id}"}}'
385
+ f' {index_id}@{{shape: cyl, label: "🗂️ {index.id}"}}'
398
386
  )
399
387
  # Connect to embedding model
400
388
  emb_model_id = f"EMB_{_sanitize_id(index.embedding_model.id)}"
@@ -404,7 +392,7 @@ def _generate_shared_resources(app: Application) -> list[str]:
404
392
  lines.append(f" {index_id} -.->|embeds| {emb_model_id}")
405
393
  elif isinstance(index, DocumentIndex):
406
394
  lines.append(
407
- f' {index_id}@{{shape: cyl, label: "📚 Docs: {index.id}"}}'
395
+ f' {index_id}@{{shape: cyl, label: "📚 {index.id}"}}'
408
396
  )
409
397
  else:
410
398
  lines.append(
@@ -412,11 +400,15 @@ def _generate_shared_resources(app: Application) -> list[str]:
412
400
  )
413
401
 
414
402
  if index.auth:
415
- # Handle auth as either AuthorizationProvider object or string ID
416
- if isinstance(index.auth, str):
417
- auth_id = f"AUTH_{_sanitize_id(index.auth)}"
403
+ auth_value = index.auth
404
+ if isinstance(auth_value, str):
405
+ auth_ref = auth_value
418
406
  else:
419
- auth_id = f"AUTH_{_sanitize_id(index.auth.id)}"
407
+ auth_ref = getattr(auth_value, "id", None)
408
+ if auth_ref is None:
409
+ auth_ref = str(auth_value)
410
+
411
+ auth_id = f"AUTH_{_sanitize_id(str(auth_ref))}"
420
412
  lines.append(f" {index_id} -.->|uses| {auth_id}")
421
413
 
422
414
  # Memories
@@ -428,7 +420,7 @@ def _generate_shared_resources(app: Application) -> list[str]:
428
420
  else str(memory.token_limit)
429
421
  )
430
422
  lines.append(
431
- f' {memory_id}@{{shape: win-pane, label: "🧠 {memory.id}\\n{token_limit}T"}}'
423
+ f' {memory_id}@{{shape: win-pane, label: "🧠 {memory.id} ({token_limit}T)"}}'
432
424
  )
433
425
 
434
426
  # Tools (if not already covered by flows)
@@ -444,7 +436,7 @@ def _generate_shared_resources(app: Application) -> list[str]:
444
436
  lines.append(f" {tool_id} -.->|uses| {auth_id}")
445
437
  elif isinstance(tool, PythonFunctionTool):
446
438
  lines.append(
447
- f' {tool_id}@{{shape: rect, label: "🐍 {tool.id}\\n{tool.function_name}"}}'
439
+ f' {tool_id}@{{shape: rect, label: "🐍 {tool.id}"}}'
448
440
  )
449
441
  else:
450
442
  lines.append(
@@ -459,7 +451,7 @@ def _generate_shared_resources(app: Application) -> list[str]:
459
451
  def _generate_telemetry_nodes(telemetry: TelemetrySink) -> list[str]:
460
452
  """Generate nodes for telemetry configuration."""
461
453
  # Replace :// with a space to avoid markdown link parsing
462
- safe_endpoint = telemetry.endpoint.replace("://", "://")
454
+ safe_endpoint = telemetry.endpoint.replace("://", "://") # type: ignore[union-attr]
463
455
 
464
456
  lines = [
465
457
  ' subgraph TELEMETRY ["📊 Observability"]',
@@ -1,57 +1,61 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qtype
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: DSL for Generative AI Prototyping
5
+ Project-URL: Homepage, https://github.com/bazaarvoice/qtype
5
6
  Author-email: Lou Kratz <lou.kratz+qtype@bazaarvoice.com>
6
7
  License-Expression: Apache-2.0
7
- Project-URL: Homepage, https://github.com/bazaarvoice/qtype
8
- Requires-Python: >=3.10
9
- Description-Content-Type: text/markdown
10
8
  License-File: LICENSE
11
- Requires-Dist: jsonschema>=4.24.0
12
- Requires-Dist: pydantic>=2.12.4
13
- Requires-Dist: pyyaml>=6.0.2
14
- Requires-Dist: python-dotenv>=1.0.0
15
- Requires-Dist: openai>=1.93.0
9
+ Requires-Python: >=3.10
16
10
  Requires-Dist: fsspec>=2025.5.1
11
+ Requires-Dist: google-cloud-aiplatform>=1.120.0
12
+ Requires-Dist: jsonschema>=4.24.0
17
13
  Requires-Dist: mkdocs-awesome-pages-plugin>=2.10.1
18
- Requires-Dist: pip-system-certs>=5.2
14
+ Requires-Dist: openai>=1.93.0
19
15
  Requires-Dist: openapi3-parser>=1.1.21
16
+ Requires-Dist: pip-system-certs>=5.2
20
17
  Requires-Dist: pydantic-yaml>=1.6.0
21
- Requires-Dist: google-cloud-aiplatform>=1.120.0
18
+ Requires-Dist: pydantic>=2.12.4
19
+ Requires-Dist: python-dotenv>=1.0.0
20
+ Requires-Dist: pyyaml>=6.0.2
22
21
  Provides-Extra: interpreter
23
- Requires-Dist: aiostream>=0.7.1; extra == "interpreter"
24
- Requires-Dist: arize-phoenix-otel>=0.12.1; extra == "interpreter"
25
- Requires-Dist: boto3>=1.34.0; extra == "interpreter"
26
- Requires-Dist: datasets>=4.4.1; extra == "interpreter"
27
- Requires-Dist: diskcache>=5.6.3; extra == "interpreter"
28
- Requires-Dist: docling>=2.55.1; extra == "interpreter"
29
- Requires-Dist: docx2txt>=0.9; extra == "interpreter"
30
- Requires-Dist: fastapi>=0.116.1; extra == "interpreter"
31
- Requires-Dist: jsonpath-ng>=1.7.0; extra == "interpreter"
32
- Requires-Dist: langfuse>=3.9.0; extra == "interpreter"
33
- Requires-Dist: llama-index-embeddings-bedrock>=0.5.2; extra == "interpreter"
34
- Requires-Dist: llama-index-embeddings-openai>=0.3.1; extra == "interpreter"
35
- Requires-Dist: llama-index-llms-bedrock-converse>=0.10.5; extra == "interpreter"
36
- Requires-Dist: llama-index-llms-bedrock>=0.3.8; extra == "interpreter"
37
- Requires-Dist: llama-index-llms-vertex>=0.6.1; extra == "interpreter"
38
- Requires-Dist: llama-index-postprocessor-bedrock-rerank>=0.5.1; extra == "interpreter"
39
- Requires-Dist: llama-index-readers-huggingface-fs>=0.4.1; extra == "interpreter"
40
- Requires-Dist: llama-index-vector-stores-qdrant>=0.8.6; extra == "interpreter"
41
- Requires-Dist: llama-index>=0.12.45; extra == "interpreter"
42
- Requires-Dist: openinference-instrumentation-llama-index>=4.3.4; extra == "interpreter"
43
- Requires-Dist: opensearch-py>=2.7.0; extra == "interpreter"
44
- Requires-Dist: opentelemetry-exporter-otlp>=1.35.0; extra == "interpreter"
45
- Requires-Dist: opentelemetry-sdk>=1.35.0; extra == "interpreter"
46
- Requires-Dist: pandas>=2.2.3; extra == "interpreter"
47
- Requires-Dist: psycopg2-binary>=2.9.10; extra == "interpreter"
48
- Requires-Dist: pyarrow>=21.0.0; extra == "interpreter"
49
- Requires-Dist: pyathena[sqlalchemy]>=3.18.0; extra == "interpreter"
50
- Requires-Dist: python-magic>=0.4.27; extra == "interpreter"
51
- Requires-Dist: s3fs>=2025.7.0; extra == "interpreter"
52
- Requires-Dist: sqlalchemy>=2.0.42; extra == "interpreter"
53
- Requires-Dist: uvicorn[standard]>=0.35.0; extra == "interpreter"
54
- Dynamic: license-file
22
+ Requires-Dist: aiostream>=0.7.1; extra == 'interpreter'
23
+ Requires-Dist: arize-phoenix-otel>=0.12.1; extra == 'interpreter'
24
+ Requires-Dist: boto3>=1.34.0; extra == 'interpreter'
25
+ Requires-Dist: datasets>=4.4.1; extra == 'interpreter'
26
+ Requires-Dist: diskcache>=5.6.3; extra == 'interpreter'
27
+ Requires-Dist: docling>=2.55.1; extra == 'interpreter'
28
+ Requires-Dist: docx2txt>=0.9; extra == 'interpreter'
29
+ Requires-Dist: fastapi>=0.116.1; extra == 'interpreter'
30
+ Requires-Dist: jsonpath-ng>=1.7.0; extra == 'interpreter'
31
+ Requires-Dist: langfuse>=3.9.0; extra == 'interpreter'
32
+ Requires-Dist: llama-index-embeddings-bedrock>=0.5.2; extra == 'interpreter'
33
+ Requires-Dist: llama-index-embeddings-openai>=0.3.1; extra == 'interpreter'
34
+ Requires-Dist: llama-index-llms-bedrock-converse>=0.10.5; extra == 'interpreter'
35
+ Requires-Dist: llama-index-llms-bedrock>=0.3.8; extra == 'interpreter'
36
+ Requires-Dist: llama-index-llms-vertex>=0.6.1; extra == 'interpreter'
37
+ Requires-Dist: llama-index-postprocessor-bedrock-rerank>=0.5.1; extra == 'interpreter'
38
+ Requires-Dist: llama-index-readers-huggingface-fs>=0.4.1; extra == 'interpreter'
39
+ Requires-Dist: llama-index-vector-stores-qdrant>=0.8.6; extra == 'interpreter'
40
+ Requires-Dist: llama-index>=0.12.45; extra == 'interpreter'
41
+ Requires-Dist: openinference-instrumentation-llama-index>=4.3.4; extra == 'interpreter'
42
+ Requires-Dist: opensearch-py>=2.7.0; extra == 'interpreter'
43
+ Requires-Dist: opentelemetry-exporter-otlp>=1.35.0; extra == 'interpreter'
44
+ Requires-Dist: opentelemetry-sdk>=1.35.0; extra == 'interpreter'
45
+ Requires-Dist: pandas>=2.2.3; extra == 'interpreter'
46
+ Requires-Dist: psycopg2-binary>=2.9.10; extra == 'interpreter'
47
+ Requires-Dist: pyarrow>=21.0.0; extra == 'interpreter'
48
+ Requires-Dist: pyathena[sqlalchemy]>=3.18.0; extra == 'interpreter'
49
+ Requires-Dist: python-magic>=0.4.27; extra == 'interpreter'
50
+ Requires-Dist: s3fs>=2025.7.0; extra == 'interpreter'
51
+ Requires-Dist: sqlalchemy>=2.0.42; extra == 'interpreter'
52
+ Requires-Dist: uvicorn[standard]>=0.35.0; extra == 'interpreter'
53
+ Provides-Extra: mcp
54
+ Requires-Dist: cachetools>=6.2.1; extra == 'mcp'
55
+ Requires-Dist: httpx>=0.28.1; extra == 'mcp'
56
+ Requires-Dist: mcp[cli]>=1.25.0; extra == 'mcp'
57
+ Requires-Dist: tantivy>=0.25.1; extra == 'mcp'
58
+ Description-Content-Type: text/markdown
55
59
 
56
60
  # QType
57
61
 
@@ -130,6 +134,23 @@ And go to [http://localhost:8000/ui](http://localhost:8000/ui) to see the user i
130
134
 
131
135
  See the [full docs](https://bazaarvoice.github.io/qtype/) for more examples and guides.
132
136
 
137
+ ## ✨ Developing with AI?
138
+
139
+ Use the QType MCP server to speed yourself up! Just set your assistant to run `qtype mcp`.
140
+ For VSCode, just add the following to `.vscode/mcp.json`:
141
+
142
+ ```json
143
+ {
144
+ "servers": {
145
+ "qtype": {
146
+ "type": "stdio",
147
+ "command": "qtype",
148
+ "cwd": "${workspaceFolder}",
149
+ "args": ["mcp", "--transport", "stdio"]
150
+ }
151
+ }
152
+ }
153
+ ```
133
154
 
134
155
 
135
156
  ## 🤝 Contributing
@@ -158,4 +179,4 @@ Stay tuned for upcoming features like:
158
179
  Happy hacking with QType! 🛠️
159
180
 
160
181
 
161
- [![Generate JSON Schema](https://github.com/bazaarvoice/qtype/actions/workflows/github_workflows_generate-schema.yml/badge.svg)](https://github.com/bazaarvoice/qtype/actions/workflows/github_workflows_generate-schema.yml) [![Publish to PyPI](https://github.com/bazaarvoice/qtype/actions/workflows/publish-pypi.yml/badge.svg)](https://github.com/bazaarvoice/qtype/actions/workflows/publish-pypi.yml)
182
+ [![Generate JSON Schema](https://github.com/bazaarvoice/qtype/actions/workflows/github_workflows_generate-schema.yml/badge.svg)](https://github.com/bazaarvoice/qtype/actions/workflows/github_workflows_generate-schema.yml) [![Publish to PyPI](https://github.com/bazaarvoice/qtype/actions/workflows/publish-pypi.yml/badge.svg)](https://github.com/bazaarvoice/qtype/actions/workflows/publish-pypi.yml)