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
@@ -0,0 +1,29 @@
1
+ # QType Examples
2
+
3
+ ## Pattern: Simple Chatbot
4
+
5
+ Basic conversational interface with memory.
6
+
7
+ ```yaml
8
+ --8<-- "../examples/conversational_ai/simple_chatbot.qtype.yaml"
9
+ ```
10
+
11
+ ---
12
+
13
+ ## Pattern: Tool-Based Research
14
+
15
+ Web search with synthesis using external tools.
16
+
17
+ ```yaml
18
+ --8<-- "../examples/research_assistant/research_assistant.qtype.yaml"
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Pattern: Data Pipeline with LLM Processing
24
+
25
+ Parallel processing of database records with LLM analysis.
26
+
27
+ ```yaml
28
+ --8<-- "../examples/data_processing/dataflow_pipelines.qtype.yaml"
29
+ ```
@@ -0,0 +1,27 @@
1
+ /* Custom styles for QType documentation */
2
+
3
+ /* Increase max width of main content area */
4
+ .md-grid {
5
+ max-width: 75rem; /* Default is 61rem (976px), this increases to 1280px */
6
+ }
7
+
8
+ /* Adjust content width for better readability */
9
+ .md-content {
10
+ max-width: none;
11
+ }
12
+
13
+ /* Ensure sidebars remain proportional */
14
+ .md-sidebar--primary {
15
+ left: 0;
16
+ }
17
+
18
+ .md-sidebar--secondary {
19
+ right: 0;
20
+ }
21
+
22
+ /* Make section headers darker gray */
23
+ .md-nav__title,
24
+ .md-nav__item--section > .md-nav__link {
25
+ color: #424242 !important;
26
+ font-weight: 600;
27
+ }
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
- return Union[resolved_type, None], True
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
- root_list = model.root # type: ignore
257
- if not isinstance(root_list, list):
258
- return
256
+ root_value = model.root # type: ignore[attr-defined]
259
257
 
260
- for i, item in enumerate(root_list):
258
+ def _resolve_root_item(item: Any) -> Any:
261
259
  match item:
262
260
  case base_types.Reference():
263
- root_list[i] = _resolve_reference(
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(
@@ -283,6 +291,10 @@ def _resolve_list_references(
283
291
  field_value[i] = _resolve_reference(
284
292
  item.ref, type(item), lookup_map
285
293
  )
294
+ case str():
295
+ # Resolve string IDs to their objects (e.g., variable IDs to Variables)
296
+ if item in lookup_map:
297
+ field_value[i] = lookup_map[item]
286
298
  case BaseModel():
287
299
  _resolve_all_references(item, lookup_map)
288
300
 
@@ -301,6 +313,10 @@ def _resolve_dict_references(
301
313
  match v:
302
314
  case base_types.Reference():
303
315
  field_value[k] = _resolve_reference(v.ref, type(v), lookup_map)
316
+ case str():
317
+ # Resolve string IDs to their objects (e.g., variable IDs to Variables)
318
+ if v in lookup_map:
319
+ field_value[k] = lookup_map[v]
304
320
  case BaseModel():
305
321
  _resolve_all_references(v, lookup_map)
306
322
 
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:default})
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:default} syntax.
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
@@ -79,6 +79,11 @@ def _resolve_list_type(
79
79
  if cls == element_type:
80
80
  return ListType(element_type=name)
81
81
  return ListType(element_type=str(element_type))
82
+ elif isinstance(element_type, type) and issubclass(
83
+ element_type, BaseModel
84
+ ):
85
+ # Custom type class - store its name as string reference
86
+ return ListType(element_type=element_type.__name__)
82
87
  else:
83
88
  raise ValueError(
84
89
  (
@@ -140,6 +145,8 @@ def _resolve_variable_type(
140
145
  Resolve a type to its corresponding representation.
141
146
 
142
147
  Handles primitive types, list types, domain types, and custom types.
148
+ Unknown types are returned as strings (forward references) and will be
149
+ validated later during the linking phase.
143
150
 
144
151
  Args:
145
152
  parsed_type: The type to resolve (can be string or already resolved)
@@ -172,17 +179,26 @@ def _resolve_variable_type(
172
179
  if custom is not None:
173
180
  return custom
174
181
 
175
- # If it's not any known type, return it as a string.
176
- # This assumes it might be a forward reference to a custom type.
177
- return parsed_type
182
+ # If it's not any known type, raise an error
183
+ available_types = (
184
+ f"primitive types ({', '.join([t.value for t in PrimitiveTypeEnum])}), "
185
+ f"domain types ({', '.join(DOMAIN_CLASSES.keys())})"
186
+ )
187
+ if custom_type_registry:
188
+ available_types += (
189
+ f", or custom types ({', '.join(custom_type_registry.keys())})"
190
+ )
191
+ raise ValueError(
192
+ f"Unknown type '{parsed_type}'. Must be one of: {available_types}"
193
+ )
178
194
 
179
195
 
180
196
  def _resolve_type_field_validator(data: Any, info: ValidationInfo) -> Any:
181
197
  """
182
198
  Shared validator for resolving 'type' fields in models.
183
199
 
184
- This validator resolves string-based type references using the custom
185
- type registry from the validation context.
200
+ This validator handles optional '?' syntax and resolves string-based
201
+ type references using the custom type registry from the validation context.
186
202
 
187
203
  Args:
188
204
  data: The data dict being validated
@@ -196,6 +212,13 @@ def _resolve_type_field_validator(data: Any, info: ValidationInfo) -> Any:
196
212
  and "type" in data
197
213
  and isinstance(data["type"], str)
198
214
  ):
215
+ # Handle '?' suffix for optional types BEFORE type resolution
216
+ type_value = data["type"]
217
+ if type_value.endswith("?"):
218
+ # Strip '?' and mark as optional
219
+ data["type"] = type_value[:-1]
220
+ data["optional"] = True
221
+
199
222
  # Get the registry of custom types from the validation context.
200
223
  custom_types = (info.context or {}).get("custom_types", {})
201
224
  resolved = _resolve_variable_type(data["type"], custom_types)
@@ -203,6 +226,54 @@ def _resolve_type_field_validator(data: Any, info: ValidationInfo) -> Any:
203
226
  return data
204
227
 
205
228
 
229
+ def _merge_vars_from_bindings(
230
+ existing: list[Reference[Variable] | str],
231
+ bindings: dict[str, Reference[Variable] | str],
232
+ ) -> list[Reference[Variable] | str]:
233
+ """Merge existing variables with bindings and deduplicate by variable ID.
234
+
235
+ Args:
236
+ existing: Existing list of variable references or IDs
237
+ bindings: Dict mapping parameter names to variable references or IDs
238
+
239
+ Returns:
240
+ Merged list with duplicates removed, preserving original form
241
+ """
242
+
243
+ def get_id(item):
244
+ return item.ref if isinstance(item, Reference) else item
245
+
246
+ seen = set()
247
+ result = []
248
+ for item in list(existing) + list(bindings.values()):
249
+ var_id = get_id(item)
250
+ if var_id not in seen:
251
+ seen.add(var_id)
252
+ result.append(item)
253
+ return result
254
+
255
+
256
+ def _type_to_string(type_value: Any) -> str:
257
+ """Convert a type value to its string representation.
258
+
259
+ Args:
260
+ type_value: The type value to convert
261
+
262
+ Returns:
263
+ String representation of the type
264
+ """
265
+ if isinstance(type_value, str):
266
+ return type_value
267
+ elif isinstance(type_value, PrimitiveTypeEnum):
268
+ return type_value.value
269
+ elif isinstance(type_value, ListType):
270
+ return str(type_value)
271
+ elif isinstance(type_value, type):
272
+ return type_value.__name__
273
+ else:
274
+ return str(type_value)
275
+
276
+
206
277
  class Variable(StrictBaseModel):
207
278
  """Schema for a variable that can serve as input, output, or parameter within the DSL."""
208
279
 
@@ -216,15 +287,37 @@ class Variable(StrictBaseModel):
216
287
  "Type of data expected or produced. Either a CustomType or domain specific type."
217
288
  ),
218
289
  )
290
+ optional: bool = Field(
291
+ default=False,
292
+ description=(
293
+ "Whether this variable can be unset or None. "
294
+ "Use '?' suffix in type string as shorthand (e.g., 'text?')."
295
+ ),
296
+ )
219
297
 
220
- ui: UIType | None = Field(None, description="Hints for the UI if needed.")
298
+ ui: UIType | None = Field(
299
+ default=None, description="Hints for the UI if needed."
300
+ )
221
301
 
222
302
  @model_validator(mode="before")
223
303
  @classmethod
224
304
  def resolve_type(cls, data: Any, info: ValidationInfo) -> Any:
225
- """Resolve string-based type references using the shared validator."""
305
+ """Resolve string-based type references and handle optional '?' syntax."""
226
306
  return _resolve_type_field_validator(data, info)
227
307
 
308
+ @model_serializer
309
+ def serialize_model(self):
310
+ """Serialize with '?' suffix for optional types."""
311
+ result: dict[str, Any] = {"id": self.id}
312
+
313
+ type_str = _type_to_string(self.type)
314
+ result["type"] = f"{type_str}?" if self.optional else type_str
315
+
316
+ if self.ui is not None:
317
+ result["ui"] = self.ui.model_dump()
318
+
319
+ return result
320
+
228
321
  @model_validator(mode="after")
229
322
  def validate_ui_type(self) -> Variable:
230
323
  """Ensure at least one credential source is provided."""
@@ -260,36 +353,6 @@ class CustomType(StrictBaseModel):
260
353
  properties: dict[str, str]
261
354
 
262
355
 
263
- class ToolParameter(BaseModel):
264
- """Defines a tool input or output parameter with type and optional flag."""
265
-
266
- type: VariableType | str
267
- optional: bool = Field(
268
- default=False, description="Whether this parameter is optional"
269
- )
270
-
271
- @model_validator(mode="before")
272
- @classmethod
273
- def resolve_type(cls, data: Any, info: ValidationInfo) -> Any:
274
- """Resolve string-based type references using the shared validator."""
275
- return _resolve_type_field_validator(data, info)
276
-
277
- @staticmethod
278
- def _serialize_type(value):
279
- if isinstance(value, type):
280
- return value.__name__
281
- elif hasattr(value, "__name__"):
282
- return value.__name__
283
- return value
284
-
285
- @model_serializer
286
- def _model_serializer(self):
287
- # Use the default serialization, but ensure 'type' is a string
288
- data = self.model_dump()
289
- data["type"] = self._serialize_type(data.get("type"))
290
- return data
291
-
292
-
293
356
  class ListType(BaseModel):
294
357
  """Represents a list type with a specific element type."""
295
358
 
@@ -417,11 +480,19 @@ class Construct(Step):
417
480
  """A step that converts variables into an instance of a Custom or Domain Type"""
418
481
 
419
482
  type: Literal["Construct"] = "Construct"
420
- field_mapping: dict[str, str] = Field(
483
+ field_bindings: dict[str, Reference[Variable] | str] = Field(
421
484
  ...,
422
- description="Mapping of type inputs to variable names, if needed.",
485
+ description="Mapping from type field names to flow variable names.",
423
486
  )
424
487
 
488
+ @model_validator(mode="after")
489
+ def infer_inputs_from_bindings(self) -> "Construct":
490
+ """Infer inputs from field bindings."""
491
+ self.inputs = _merge_vars_from_bindings(
492
+ self.inputs, self.field_bindings
493
+ )
494
+ return self
495
+
425
496
 
426
497
  class PromptTemplate(Step):
427
498
  """Defines a prompt template with a string format and variable bindings.
@@ -444,12 +515,12 @@ class Tool(StrictBaseModel, ABC):
444
515
  description: str = Field(
445
516
  ..., description="Description of what the tool does."
446
517
  )
447
- inputs: dict[str, ToolParameter] = Field(
448
- default_factory=dict,
518
+ inputs: list[Variable] = Field(
519
+ default_factory=list,
449
520
  description="Input parameters required by this tool.",
450
521
  )
451
- outputs: dict[str, ToolParameter] = Field(
452
- default_factory=dict,
522
+ outputs: list[Variable] = Field(
523
+ default_factory=list,
453
524
  description="Output parameters produced by this tool.",
454
525
  )
455
526
 
@@ -484,9 +555,9 @@ class APITool(Tool):
484
555
  default_factory=dict,
485
556
  description="Optional HTTP headers to include in the request.",
486
557
  )
487
- parameters: dict[str, ToolParameter] = Field(
488
- default_factory=dict,
489
- description="Output parameters produced by this tool.",
558
+ parameters: list[Variable] = Field(
559
+ default_factory=list,
560
+ description="Path and query parameters for the API call.",
490
561
  )
491
562
 
492
563
 
@@ -621,6 +692,9 @@ class FieldExtractor(Step):
621
692
  The extracted data is used to construct the output variable by passing it
622
693
  as keyword arguments to the output type's constructor.
623
694
 
695
+ If there is no match and the output variable is optional, it is set to None.
696
+ If there is no match and the output variable is required, an error is raised.
697
+
624
698
  Example JSONPath expressions:
625
699
  - `$.field_name` - Extract a single field
626
700
  - `$.items[*]` - Extract all items from a list
@@ -632,10 +706,6 @@ class FieldExtractor(Step):
632
706
  ...,
633
707
  description="JSONPath expression to extract data from the input. Uses jsonpath-ng syntax.",
634
708
  )
635
- fail_on_missing: bool = Field(
636
- default=True,
637
- description="Whether to raise an error if the JSONPath matches no data. If False, returns None.",
638
- )
639
709
 
640
710
 
641
711
  class InvokeTool(Step, ConcurrentStepMixin):
@@ -647,15 +717,25 @@ class InvokeTool(Step, ConcurrentStepMixin):
647
717
  ...,
648
718
  description="Tool to invoke.",
649
719
  )
650
- input_bindings: dict[str, str] = Field(
720
+ input_bindings: dict[str, Reference[Variable] | str] = Field(
651
721
  ...,
652
- description="Mapping from variable references to tool input parameter names.",
722
+ description="Mapping from tool parameter names to flow variable names.",
653
723
  )
654
- output_bindings: dict[str, str] = Field(
724
+ output_bindings: dict[str, Reference[Variable] | str] = Field(
655
725
  ...,
656
- description="Mapping from variable references to tool output parameter names.",
726
+ description="Mapping from tool output names to flow variable names.",
657
727
  )
658
728
 
729
+ @model_validator(mode="after")
730
+ def infer_inputs_outputs_from_bindings(self) -> "InvokeTool":
731
+ self.inputs = _merge_vars_from_bindings(
732
+ self.inputs, self.input_bindings
733
+ )
734
+ self.outputs = _merge_vars_from_bindings(
735
+ self.outputs, self.output_bindings
736
+ )
737
+ return self
738
+
659
739
 
660
740
  class InvokeFlow(Step):
661
741
  """Invokes a flow with input and output bindings."""
@@ -666,15 +746,25 @@ class InvokeFlow(Step):
666
746
  ...,
667
747
  description="Flow to invoke.",
668
748
  )
669
- input_bindings: dict[Reference[Variable], str] = Field(
749
+ input_bindings: dict[str, Reference[Variable] | str] = Field(
670
750
  ...,
671
- description="Mapping from variable references to flow input variable IDs.",
751
+ description="Mapping from flow input variable IDs to step variable names.",
672
752
  )
673
- output_bindings: dict[Reference[Variable], str] = Field(
753
+ output_bindings: dict[str, Reference[Variable] | str] = Field(
674
754
  ...,
675
- description="Mapping from variable references to flow output variable IDs.",
755
+ description="Mapping from flow output variable IDs to step variable names.",
676
756
  )
677
757
 
758
+ @model_validator(mode="after")
759
+ def infer_inputs_outputs_from_bindings(self) -> "InvokeFlow":
760
+ self.inputs = _merge_vars_from_bindings(
761
+ self.inputs, self.input_bindings
762
+ )
763
+ self.outputs = _merge_vars_from_bindings(
764
+ self.outputs, self.output_bindings
765
+ )
766
+ return self
767
+
678
768
 
679
769
  #
680
770
  # ---------------- Secret Manager Component ----------------
@@ -979,6 +1069,21 @@ class FileSource(Source):
979
1069
  description="Reference to a variable with an fsspec-compatible URI to read from, or the uri itself.",
980
1070
  )
981
1071
 
1072
+ @model_validator(mode="after")
1073
+ def infer_inputs_from_path(self) -> "FileSource":
1074
+ """Add path variable to inputs if it's a variable reference."""
1075
+ if isinstance(self.path, str):
1076
+ # Path is a variable ID, add it to inputs
1077
+ path_ref = Reference[Variable].model_validate({"$ref": self.path})
1078
+ if path_ref not in self.inputs and self.path not in self.inputs:
1079
+ self.inputs = list(self.inputs) + [path_ref]
1080
+ elif isinstance(self.path, Reference):
1081
+ # Path is already a Reference, add it to inputs
1082
+ if self.path not in self.inputs:
1083
+ self.inputs = list(self.inputs) + [self.path]
1084
+ # If path is ConstantPath, don't add to inputs
1085
+ return self
1086
+
982
1087
 
983
1088
  class Writer(Step, BatchableStepMixin):
984
1089
  """Base class for things that write data in batches."""
@@ -999,22 +1104,31 @@ class FileWriter(Writer, BatchableStepMixin):
999
1104
  description="Configuration for processing the input stream in batches. If omitted, the step processes items one by one.",
1000
1105
  )
1001
1106
 
1107
+ @model_validator(mode="after")
1108
+ def infer_inputs_from_path(self) -> "FileWriter":
1109
+ """Add path variable to inputs if it's a variable reference."""
1110
+ if isinstance(self.path, str):
1111
+ # Path is a variable ID, add it to inputs
1112
+ path_ref = Reference[Variable].model_validate({"$ref": self.path})
1113
+ if path_ref not in self.inputs and self.path not in self.inputs:
1114
+ self.inputs = list(self.inputs) + [path_ref]
1115
+ elif isinstance(self.path, Reference):
1116
+ # Path is already a Reference, add it to inputs
1117
+ if self.path not in self.inputs:
1118
+ self.inputs = list(self.inputs) + [self.path]
1119
+ # If path is ConstantPath, don't add to inputs
1120
+ return self
1121
+
1002
1122
 
1003
1123
  class Aggregate(Step):
1004
1124
  """
1005
- A terminal step that consumes an entire input stream and produces a single
1006
- summary message with success/error counts.
1125
+ A step that, after all messages have been processed,
1126
+ returns a single message containing the counts of successful and failed
1127
+ messages. Other messages are passed through unchanged.
1007
1128
  """
1008
1129
 
1009
1130
  type: Literal["Aggregate"] = "Aggregate"
1010
1131
 
1011
- # Outputs are now optional. The user can provide 0, 1, 2, or 3 names.
1012
- # The order will be: success_count, error_count, total_count
1013
- outputs: list[Reference[Variable] | str] = Field(
1014
- default_factory=list,
1015
- description="References to the variables for the output. There should be one and only one output with type AggregateStats",
1016
- )
1017
-
1018
1132
 
1019
1133
  #
1020
1134
  # ---------------- Retrieval Augmented Generation Components ----------------
@@ -0,0 +1,63 @@
1
+ id: aws_auth_demo
2
+ description: |
3
+ Demonstrates different methods of AWS authentication for Bedrock models.
4
+ Choose the method that best fits your deployment environment.
5
+
6
+ # Method 1: AWS Profile (recommended for local development)
7
+ # Uses credentials from ~/.aws/credentials
8
+ auths:
9
+ - type: aws
10
+ id: aws_profile
11
+ profile_name: default
12
+ region: us-east-1
13
+
14
+ # Method 2: Environment variables (recommended for production)
15
+ # Set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION in environment
16
+ # Uncomment to use:
17
+ # - type: aws
18
+ # id: aws_env
19
+ # access_key_id: ${AWS_ACCESS_KEY_ID}
20
+ # secret_access_key: ${AWS_SECRET_ACCESS_KEY}
21
+ # region: us-east-1
22
+
23
+ # Method 3: Role assumption (for cross-account access)
24
+ # Uncomment and configure to use:
25
+ # - type: aws
26
+ # id: aws_role
27
+ # profile_name: base_profile
28
+ # role_arn: arn:aws:iam::123456789012:role/BedrockAccessRole
29
+ # role_session_name: qtype-app-session
30
+ # region: us-east-1
31
+
32
+ models:
33
+ - type: Model
34
+ id: nova_micro
35
+ provider: aws-bedrock
36
+ model_id: us.amazon.nova-micro-v1:0
37
+ auth: aws_profile # Change to aws_env, aws_keys, or aws_role as needed
38
+
39
+ flows:
40
+ - id: simple_completion
41
+ variables:
42
+ - id: user_prompt
43
+ type: text
44
+ - id: formatted
45
+ type: text
46
+ - id: response
47
+ type: text
48
+ inputs:
49
+ - user_prompt
50
+ outputs:
51
+ - response
52
+ steps:
53
+ - type: PromptTemplate
54
+ id: format_prompt
55
+ template: "{user_prompt}"
56
+ inputs: [user_prompt]
57
+ outputs: [formatted]
58
+
59
+ - type: LLMInference
60
+ id: generate
61
+ model: nova_micro
62
+ inputs: [formatted]
63
+ outputs: [response]
@@ -0,0 +1,43 @@
1
+ id: hello_world
2
+ description: A simple chat flow with OpenAI
3
+ models:
4
+ - type: Model
5
+ id: gpt4
6
+ provider: openai
7
+ model_id: gpt-4
8
+ inference_params:
9
+ temperature: 0.7
10
+ max_tokens: 512
11
+ auth: openai_auth
12
+ auths:
13
+ - type: api_key
14
+ id: openai_auth
15
+ api_key: ${OPENAI_KEY}
16
+ host: https://api.openai.com
17
+ memories:
18
+ - id: chat_memory
19
+ token_limit: 10000
20
+ flows:
21
+ - type: Flow
22
+ id: chat_example
23
+ interface:
24
+ type: Conversational
25
+ variables:
26
+ - id: user_message
27
+ type: ChatMessage
28
+ - id: response
29
+ type: ChatMessage
30
+ inputs:
31
+ - user_message
32
+ outputs:
33
+ - response
34
+ steps:
35
+ - id: llm_inference_step
36
+ type: LLMInference
37
+ model: gpt4
38
+ system_message: "You are a helpful assistant."
39
+ memory: chat_memory
40
+ inputs:
41
+ - user_message
42
+ outputs:
43
+ - response
@@ -0,0 +1,40 @@
1
+ id: simple_chatbot
2
+ description: A friendly chatbot with conversation memory using AWS Bedrock
3
+ models:
4
+ - type: Model
5
+ id: nova_lite
6
+ provider: aws-bedrock
7
+ model_id: amazon.nova-lite-v1:0
8
+ inference_params:
9
+ temperature: 0.7
10
+ max_tokens: 512
11
+ memories:
12
+ - id: conversation_memory
13
+ token_limit: 10000
14
+ flows:
15
+ - type: Flow
16
+ id: chat_flow
17
+ interface:
18
+ type: Conversational
19
+ variables:
20
+ - id: user_message
21
+ type: ChatMessage
22
+ - id: assistant_response
23
+ type: ChatMessage
24
+ inputs:
25
+ - user_message
26
+ outputs:
27
+ - assistant_response
28
+ steps:
29
+ - id: generate_response
30
+ type: LLMInference
31
+ model: nova_lite
32
+ system_message: |
33
+ You are a friendly and helpful chatbot. You have a warm, conversational
34
+ tone and enjoy helping users with their questions. You remember context
35
+ from previous messages in the conversation.
36
+ memory: conversation_memory
37
+ inputs:
38
+ - user_message
39
+ outputs:
40
+ - assistant_response