qtype 0.1.12__py3-none-any.whl → 0.1.14__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 (272) hide show
  1. qtype/` +0 -0
  2. qtype/application/__init__.py +0 -2
  3. qtype/application/converters/tools_from_api.py +28 -22
  4. qtype/application/converters/tools_from_module.py +66 -32
  5. qtype/base/__init__.py +8 -2
  6. qtype/base/logging.py +0 -17
  7. qtype/base/resources.py +193 -0
  8. qtype/cli.py +5 -9
  9. qtype/commands/generate.py +95 -7
  10. qtype/commands/run.py +153 -54
  11. qtype/docs/.pages +8 -0
  12. {docs → qtype/docs}/Concepts/mental-model-and-philosophy.md +1 -1
  13. qtype/docs/Contributing/.pages +4 -0
  14. {docs → qtype/docs}/Contributing/index.md +8 -1
  15. {docs → qtype/docs}/Gallery/dataflow_pipelines.md +18 -4
  16. qtype/docs/Gallery/recipe_chatbot.md +103 -0
  17. qtype/docs/Gallery/recipe_chatbot.mermaid +62 -0
  18. qtype/docs/Gallery/recipe_chatbot.png +0 -0
  19. {docs → qtype/docs}/Gallery/research_assistant.md +4 -5
  20. {docs → qtype/docs}/Gallery/simple_chatbot.md +3 -1
  21. {docs → qtype/docs}/How To/Authentication/configure_aws_authentication.md +2 -2
  22. {docs → qtype/docs}/How To/Authentication/use_api_key_authentication.md +2 -2
  23. {docs → qtype/docs}/How To/Command Line Usage/load_multiple_inputs_from_files.md +24 -9
  24. {docs → qtype/docs}/How To/Command Line Usage/pass_inputs_on_the_cli.md +7 -4
  25. {docs → qtype/docs}/How To/Command Line Usage/serve_with_auto_reload.md +3 -2
  26. {docs → qtype/docs}/How To/Data Processing/adjust_concurrency.md +3 -4
  27. {docs → qtype/docs}/How To/Data Processing/cache_step_results.md +2 -2
  28. {docs → qtype/docs}/How To/Data Processing/decode_json_xml.md +1 -1
  29. {docs → qtype/docs}/How To/Data Processing/explode_collections.md +2 -2
  30. {docs → qtype/docs}/How To/Data Processing/gather_results.md +4 -4
  31. qtype/docs/How To/Data Processing/invoke_other_flows.md +71 -0
  32. qtype/docs/How To/Data Processing/load_data_from_athena.md +49 -0
  33. qtype/docs/How To/Data Processing/load_documents.md +74 -0
  34. qtype/docs/How To/Data Processing/read_data_from_files.md +61 -0
  35. {docs → qtype/docs}/How To/Data Processing/read_sql_databases.md +4 -3
  36. {docs → qtype/docs}/How To/Data Processing/write_data_to_file.md +1 -2
  37. {docs → qtype/docs}/How To/Invoke Models/call_large_language_models.md +1 -1
  38. {docs → qtype/docs}/How To/Invoke Models/create_embeddings.md +1 -1
  39. {docs → qtype/docs}/How To/Invoke Models/reuse_prompts_with_templates.md +2 -3
  40. {docs → qtype/docs}/How To/Language Features/include_raw_text_from_other_files.md +2 -1
  41. {docs → qtype/docs}/How To/Language Features/reference_entities_by_id.md +2 -2
  42. qtype/docs/How To/Language Features/use_agent_skills.md +29 -0
  43. {docs → qtype/docs}/How To/Language Features/use_environment_variables.md +2 -1
  44. qtype/docs/How To/Language Features/use_optional_variables.md +42 -0
  45. {docs → qtype/docs}/How To/Language Features/use_qtype_mcp.md +4 -4
  46. {docs → qtype/docs}/How To/Observability & Debugging/trace_calls_with_open_telemetry.md +1 -1
  47. {docs → qtype/docs}/How To/Observability & Debugging/validate_qtype_yaml.md +3 -2
  48. {docs → qtype/docs}/How To/Observability & Debugging/visualize_application_architecture.md +1 -1
  49. {docs → qtype/docs}/How To/Qtype Server/serve_flows_as_apis.md +3 -3
  50. {docs → qtype/docs}/How To/Qtype Server/serve_flows_as_ui.md +2 -3
  51. {docs → qtype/docs}/How To/Qtype Server/use_conversational_interfaces.md +1 -4
  52. {docs → qtype/docs}/How To/Qtype Server/use_variables_with_ui_hints.md +3 -2
  53. {docs → qtype/docs}/How To/Tools & Integration/bind_tool_inputs_and_outputs.md +1 -2
  54. {docs → qtype/docs}/How To/Tools & Integration/create_tools_from_openapi_specifications.md +10 -14
  55. {docs → qtype/docs}/How To/Tools & Integration/create_tools_from_python_modules.md +5 -8
  56. {docs → qtype/docs}/Reference/cli.md +16 -17
  57. qtype/docs/Tutorials/.pages +1 -0
  58. {docs → qtype/docs}/Tutorials/01-first-qtype-application.md +4 -3
  59. {docs → qtype/docs}/Tutorials/02-conversational-chatbot.md +3 -3
  60. {docs → qtype/docs}/Tutorials/03-structured-data.md +10 -11
  61. {docs → qtype/docs}/Tutorials/04-tools-and-function-calling.md +13 -20
  62. {docs → qtype/docs}/components/APITool.md +1 -1
  63. qtype/docs/components/Aggregate.md +7 -0
  64. qtype/docs/components/Collect.md +6 -0
  65. qtype/docs/components/Construct.md +6 -0
  66. {docs → qtype/docs}/components/DocumentEmbedder.md +0 -1
  67. {docs → qtype/docs}/components/DocumentSplitter.md +0 -1
  68. qtype/docs/components/Explode.md +5 -0
  69. {docs → qtype/docs}/components/FieldExtractor.md +2 -1
  70. qtype/docs/components/InvokeFlow.md +8 -0
  71. qtype/docs/components/InvokeTool.md +8 -0
  72. {docs → qtype/docs}/components/PrimitiveTypeEnum.md +0 -1
  73. {docs → qtype/docs}/components/Source.md +0 -1
  74. {docs → qtype/docs}/components/Step.md +0 -1
  75. {docs → qtype/docs}/components/Tool.md +2 -2
  76. {docs → qtype/docs}/components/Variable.md +2 -0
  77. qtype/docs/legacy_how_tos/.pages +6 -0
  78. qtype/docs/skills/architect/SKILL.md +188 -0
  79. qtype/docs/skills/architect/references/cheatsheet.md +198 -0
  80. qtype/docs/skills/architect/references/patterns.md +29 -0
  81. qtype/docs/stylesheets/extra.css +27 -0
  82. qtype/dsl/linker.py +8 -0
  83. qtype/dsl/model.py +177 -84
  84. qtype/examples/conversational_ai/simple_chatbot_with_auth.qtype.yaml +48 -0
  85. qtype/examples/data_processing/athena_query.qtype.yaml +56 -0
  86. qtype/examples/data_processing/batch_inputs.csv +5 -0
  87. qtype/examples/data_processing/create_sample_db.py +129 -0
  88. qtype/examples/data_processing/invoke_other_flows.qtype.yaml +98 -0
  89. qtype/examples/data_processing/load_documents.qtype.yaml +31 -0
  90. qtype/examples/data_processing/reviews.db +0 -0
  91. qtype/examples/data_processing/sample_article.txt +1 -0
  92. qtype/examples/data_processing/sample_documents.jsonl +5 -0
  93. qtype/examples/invoke_models/invoke_embedding_aws.qtype.yaml +45 -0
  94. qtype/examples/language_features/optional_variables.qtype.yaml +32 -0
  95. qtype/examples/language_features/story_prompt.txt +6 -0
  96. qtype/examples/legacy/data/customers.csv +6 -0
  97. qtype/examples/legacy/echo/readme.md +29 -0
  98. qtype/examples/legacy/qtype_plugin_example.py +51 -0
  99. qtype/examples/legacy/sample_data.txt +43 -0
  100. qtype/examples/legacy/vertex/README.md +11 -0
  101. qtype/examples/rag/recipe_chatbot.qtype.yaml +216 -0
  102. qtype/examples/research_assistant/tavily.qtype.yaml +216 -0
  103. {examples → qtype/examples}/tutorials/03_structured_data.qtype.yaml +2 -2
  104. {examples → qtype/examples}/tutorials/04_tools_and_function_calling.qtype.yaml +5 -5
  105. qtype/interpreter/auth/aws.py +94 -17
  106. qtype/interpreter/auth/generic.py +11 -12
  107. qtype/interpreter/base/secrets.py +4 -2
  108. qtype/interpreter/base/stream_emitter.py +19 -13
  109. qtype/interpreter/conversions.py +15 -14
  110. qtype/interpreter/converters.py +142 -26
  111. qtype/interpreter/executors/agent_executor.py +2 -3
  112. qtype/interpreter/executors/aggregate_executor.py +3 -4
  113. qtype/interpreter/executors/bedrock_reranker_executor.py +17 -28
  114. qtype/interpreter/executors/construct_executor.py +15 -15
  115. qtype/interpreter/executors/doc_to_text_executor.py +1 -3
  116. qtype/interpreter/executors/document_embedder_executor.py +1 -12
  117. qtype/interpreter/executors/field_extractor_executor.py +13 -12
  118. qtype/interpreter/executors/file_source_executor.py +18 -31
  119. qtype/interpreter/executors/invoke_embedding_executor.py +24 -37
  120. qtype/interpreter/executors/invoke_flow_executor.py +2 -2
  121. qtype/interpreter/executors/invoke_tool_executor.py +19 -18
  122. qtype/interpreter/executors/llm_inference_executor.py +18 -18
  123. qtype/interpreter/executors/prompt_template_executor.py +1 -3
  124. qtype/interpreter/executors/sql_source_executor.py +6 -2
  125. qtype/interpreter/flow.py +11 -1
  126. qtype/interpreter/tools/function_tool_helper.py +11 -10
  127. qtype/interpreter/types.py +89 -4
  128. qtype/interpreter/typing.py +31 -32
  129. qtype/mcp/server.py +194 -86
  130. {schema → qtype/schema}/qtype.schema.json +77 -79
  131. qtype/semantic/checker.py +19 -0
  132. qtype/semantic/generate.py +3 -6
  133. qtype/semantic/model.py +26 -33
  134. qtype/semantic/resolver.py +7 -0
  135. qtype/semantic/visualize.py +18 -6
  136. {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/METADATA +47 -46
  137. qtype-0.1.14.dist-info/RECORD +361 -0
  138. {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/WHEEL +1 -2
  139. docs/How To/Data Processing/read_data_from_files.md +0 -35
  140. docs/components/Aggregate.md +0 -8
  141. docs/components/InvokeFlow.md +0 -8
  142. docs/components/InvokeTool.md +0 -8
  143. docs/components/ToolParameter.md +0 -6
  144. examples/research_assistant/tavily.qtype.yaml +0 -289
  145. qtype/application/facade.py +0 -177
  146. qtype-0.1.12.dist-info/RECORD +0 -325
  147. qtype-0.1.12.dist-info/top_level.txt +0 -1
  148. {docs → qtype/docs}/Contributing/roadmap.md +0 -0
  149. {docs → qtype/docs}/Decisions/ADR-001-Chat-vs-Completion-Endpoint-Features.md +0 -0
  150. {docs → qtype/docs}/Gallery/dataflow_pipelines.mermaid +0 -0
  151. {docs → qtype/docs}/Gallery/research_assistant.mermaid +0 -0
  152. {docs → qtype/docs}/Gallery/simple_chatbot.mermaid +0 -0
  153. {docs → qtype/docs}/How To/Language Features/include_qtype_yaml.md +0 -0
  154. {docs → qtype/docs}/How To/Observability & Debugging/visualize_example.mermaid +0 -0
  155. {docs → qtype/docs}/How To/Qtype Server/flow_as_ui.png +0 -0
  156. {docs → qtype/docs}/Reference/plugins.md +0 -0
  157. {docs → qtype/docs}/Reference/semantic-validation-rules.md +0 -0
  158. {docs → qtype/docs}/Tutorials/example_chat.png +0 -0
  159. {docs → qtype/docs}/Tutorials/index.md +0 -0
  160. {docs → qtype/docs}/components/APIKeyAuthProvider.md +0 -0
  161. {docs → qtype/docs}/components/AWSAuthProvider.md +0 -0
  162. {docs → qtype/docs}/components/AWSSecretManager.md +0 -0
  163. {docs → qtype/docs}/components/Agent.md +0 -0
  164. {docs → qtype/docs}/components/AggregateStats.md +0 -0
  165. {docs → qtype/docs}/components/Application.md +0 -0
  166. {docs → qtype/docs}/components/AuthorizationProvider.md +0 -0
  167. {docs → qtype/docs}/components/AuthorizationProviderList.md +0 -0
  168. {docs → qtype/docs}/components/BearerTokenAuthProvider.md +0 -0
  169. {docs → qtype/docs}/components/BedrockReranker.md +0 -0
  170. {docs → qtype/docs}/components/ChatContent.md +0 -0
  171. {docs → qtype/docs}/components/ChatMessage.md +0 -0
  172. {docs → qtype/docs}/components/ConstantPath.md +0 -0
  173. {docs → qtype/docs}/components/CustomType.md +0 -0
  174. {docs → qtype/docs}/components/Decoder.md +0 -0
  175. {docs → qtype/docs}/components/DecoderFormat.md +0 -0
  176. {docs → qtype/docs}/components/DocToTextConverter.md +0 -0
  177. {docs → qtype/docs}/components/Document.md +0 -0
  178. {docs → qtype/docs}/components/DocumentIndex.md +0 -0
  179. {docs → qtype/docs}/components/DocumentSearch.md +0 -0
  180. {docs → qtype/docs}/components/DocumentSource.md +0 -0
  181. {docs → qtype/docs}/components/Echo.md +0 -0
  182. {docs → qtype/docs}/components/Embedding.md +0 -0
  183. {docs → qtype/docs}/components/EmbeddingModel.md +0 -0
  184. {docs → qtype/docs}/components/FileSource.md +0 -0
  185. {docs → qtype/docs}/components/FileWriter.md +0 -0
  186. {docs → qtype/docs}/components/Flow.md +0 -0
  187. {docs → qtype/docs}/components/FlowInterface.md +0 -0
  188. {docs → qtype/docs}/components/Index.md +0 -0
  189. {docs → qtype/docs}/components/IndexUpsert.md +0 -0
  190. {docs → qtype/docs}/components/InvokeEmbedding.md +0 -0
  191. {docs → qtype/docs}/components/LLMInference.md +0 -0
  192. {docs → qtype/docs}/components/ListType.md +0 -0
  193. {docs → qtype/docs}/components/Memory.md +0 -0
  194. {docs → qtype/docs}/components/MessageRole.md +0 -0
  195. {docs → qtype/docs}/components/Model.md +0 -0
  196. {docs → qtype/docs}/components/ModelList.md +0 -0
  197. {docs → qtype/docs}/components/OAuth2AuthProvider.md +0 -0
  198. {docs → qtype/docs}/components/PromptTemplate.md +0 -0
  199. {docs → qtype/docs}/components/PythonFunctionTool.md +0 -0
  200. {docs → qtype/docs}/components/RAGChunk.md +0 -0
  201. {docs → qtype/docs}/components/RAGDocument.md +0 -0
  202. {docs → qtype/docs}/components/RAGSearchResult.md +0 -0
  203. {docs → qtype/docs}/components/Reranker.md +0 -0
  204. {docs → qtype/docs}/components/SQLSource.md +0 -0
  205. {docs → qtype/docs}/components/Search.md +0 -0
  206. {docs → qtype/docs}/components/SearchResult.md +0 -0
  207. {docs → qtype/docs}/components/SecretManager.md +0 -0
  208. {docs → qtype/docs}/components/SecretReference.md +0 -0
  209. {docs → qtype/docs}/components/TelemetrySink.md +0 -0
  210. {docs → qtype/docs}/components/ToolList.md +0 -0
  211. {docs → qtype/docs}/components/TypeList.md +0 -0
  212. {docs → qtype/docs}/components/VariableList.md +0 -0
  213. {docs → qtype/docs}/components/VectorIndex.md +0 -0
  214. {docs → qtype/docs}/components/VectorSearch.md +0 -0
  215. {docs → qtype/docs}/components/VertexAuthProvider.md +0 -0
  216. {docs → qtype/docs}/components/Writer.md +0 -0
  217. {docs → qtype/docs}/example_ui.png +0 -0
  218. {docs → qtype/docs}/index.md +0 -0
  219. {docs → qtype/docs}/legacy_how_tos/Configuration/modular-yaml.md +0 -0
  220. {docs → qtype/docs}/legacy_how_tos/Configuration/phoenix_projects.png +0 -0
  221. {docs → qtype/docs}/legacy_how_tos/Configuration/phoenix_traces.png +0 -0
  222. {docs → qtype/docs}/legacy_how_tos/Configuration/reference-by-id.md +0 -0
  223. {docs → qtype/docs}/legacy_how_tos/Configuration/telemetry-setup.md +0 -0
  224. {docs → qtype/docs}/legacy_how_tos/Data Types/custom-types.md +0 -0
  225. {docs → qtype/docs}/legacy_how_tos/Data Types/domain-types.md +0 -0
  226. {docs → qtype/docs}/legacy_how_tos/Debugging/visualize-apps.md +0 -0
  227. {docs → qtype/docs}/legacy_how_tos/Tools/api-tools.md +0 -0
  228. {docs → qtype/docs}/legacy_how_tos/Tools/python-tools.md +0 -0
  229. {examples → qtype/examples}/authentication/aws_authentication.qtype.yaml +0 -0
  230. {examples → qtype/examples}/conversational_ai/hello_world_chat.qtype.yaml +0 -0
  231. {examples → qtype/examples}/conversational_ai/simple_chatbot.qtype.yaml +0 -0
  232. {examples → qtype/examples}/data_processing/batch_processing.qtype.yaml +0 -0
  233. {examples → qtype/examples}/data_processing/cache_step_results.qtype.yaml +0 -0
  234. {examples → qtype/examples}/data_processing/collect_results.qtype.yaml +0 -0
  235. {examples → qtype/examples}/data_processing/dataflow_pipelines.qtype.yaml +0 -0
  236. {examples → qtype/examples}/data_processing/decode_json.qtype.yaml +0 -0
  237. {examples → qtype/examples}/data_processing/explode_items.qtype.yaml +0 -0
  238. {examples → qtype/examples}/data_processing/read_file.qtype.yaml +0 -0
  239. {examples → qtype/examples}/invoke_models/create_embeddings.qtype.yaml +0 -0
  240. {examples → qtype/examples}/invoke_models/simple_llm_call.qtype.yaml +0 -0
  241. {examples → qtype/examples}/language_features/include_raw.qtype.yaml +0 -0
  242. {examples → qtype/examples}/language_features/ui_hints.qtype.yaml +0 -0
  243. {examples → qtype/examples}/legacy/bedrock/data_analysis_with_telemetry.qtype.yaml +0 -0
  244. {examples → qtype/examples}/legacy/bedrock/hello_world.qtype.yaml +0 -0
  245. {examples → qtype/examples}/legacy/bedrock/hello_world_chat.qtype.yaml +0 -0
  246. {examples → qtype/examples}/legacy/bedrock/hello_world_chat_with_telemetry.qtype.yaml +0 -0
  247. {examples → qtype/examples}/legacy/bedrock/hello_world_chat_with_thinking.qtype.yaml +0 -0
  248. {examples → qtype/examples}/legacy/bedrock/hello_world_completion.qtype.yaml +0 -0
  249. {examples → qtype/examples}/legacy/bedrock/hello_world_completion_with_auth.qtype.yaml +0 -0
  250. {examples → qtype/examples}/legacy/bedrock/simple_agent_chat.qtype.yaml +0 -0
  251. {examples → qtype/examples}/legacy/chat_with_langfuse.qtype.yaml +0 -0
  252. {examples → qtype/examples}/legacy/data_processor.qtype.yaml +0 -0
  253. {examples → qtype/examples}/legacy/echo/debug_example.qtype.yaml +0 -0
  254. {examples → qtype/examples}/legacy/echo/prompt.qtype.yaml +0 -0
  255. {examples → qtype/examples}/legacy/echo/test.qtype.yaml +0 -0
  256. {examples → qtype/examples}/legacy/echo/video.qtype.yaml +0 -0
  257. {examples → qtype/examples}/legacy/field_extractor_example.qtype.yaml +0 -0
  258. {examples → qtype/examples}/legacy/multi_flow_example.qtype.yaml +0 -0
  259. {examples → qtype/examples}/legacy/openai/hello_world_chat.qtype.yaml +0 -0
  260. {examples → qtype/examples}/legacy/openai/hello_world_chat_with_telemetry.qtype.yaml +0 -0
  261. {examples → qtype/examples}/legacy/rag.qtype.yaml +0 -0
  262. {examples → qtype/examples}/legacy/time_utilities.qtype.yaml +0 -0
  263. {examples → qtype/examples}/legacy/vertex/hello_world_chat.qtype.yaml +0 -0
  264. {examples → qtype/examples}/legacy/vertex/hello_world_completion.qtype.yaml +0 -0
  265. {examples → qtype/examples}/legacy/vertex/hello_world_completion_with_auth.qtype.yaml +0 -0
  266. {examples → qtype/examples}/observability_debugging/trace_with_opentelemetry.qtype.yaml +0 -0
  267. {examples → qtype/examples}/research_assistant/research_assistant.qtype.yaml +0 -0
  268. {examples → qtype/examples}/research_assistant/tavily.oas.yaml +0 -0
  269. {examples → qtype/examples}/tutorials/01_hello_world.qtype.yaml +0 -0
  270. {examples → qtype/examples}/tutorials/02_conversational_chat.qtype.yaml +0 -0
  271. {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/entry_points.txt +0 -0
  272. {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  from typing import AsyncIterator
2
3
 
3
4
  from llama_cloud import MessageRole as LlamaMessageRole
@@ -85,6 +86,7 @@ class LLMInferenceExecutor(StepExecutor):
85
86
 
86
87
  except Exception as e:
87
88
  # Emit error event to stream so frontend can display it
89
+ logging.error(f"LLM Inference search failed: {e}", exc_info=True)
88
90
  await self.stream_emitter.error(str(e))
89
91
  yield message.copy_with_error(self.step.id, e)
90
92
 
@@ -109,7 +111,7 @@ class LLMInferenceExecutor(StepExecutor):
109
111
  # Convert input variables to chat messages
110
112
  inputs = []
111
113
  for input_var in self.step.inputs:
112
- value = message.variables.get(input_var.id)
114
+ value = message.get_variable(input_var.id)
113
115
  # Convert any value type to ChatMessage, then to LlamaChatMessage
114
116
  chat_msg = variable_to_chat_message(value, input_var)
115
117
  inputs.append(to_chat_message(chat_msg))
@@ -160,9 +162,14 @@ class LLMInferenceExecutor(StepExecutor):
160
162
  if self.context.on_stream_event:
161
163
  # Generate a unique stream ID for this inference
162
164
  stream_id = f"llm-{self.step.id}-{id(message)}"
163
- async with self.stream_emitter.reasoning_stream(
164
- f"llm-{self.step.id}-{id(message)}-reasoning"
165
- ) as reasoning:
165
+ reasoning_stream_id = f"llm-{self.step.id}-{id(message)}-reasoning"
166
+
167
+ async with (
168
+ self.stream_emitter.reasoning_stream(
169
+ reasoning_stream_id
170
+ ) as reasoning,
171
+ self.stream_emitter.text_stream(stream_id) as streamer,
172
+ ):
166
173
  generator = await model.astream_chat(
167
174
  messages=inputs,
168
175
  **(
@@ -171,26 +178,19 @@ class LLMInferenceExecutor(StepExecutor):
171
178
  else {}
172
179
  ),
173
180
  )
174
- async for complete_response in generator:
181
+ async for chat_response in generator:
182
+ # Extract and emit reasoning if present
175
183
  reasoning_text = self.__extract_stream_reasoning_(
176
- complete_response
184
+ chat_response
177
185
  )
178
186
  if reasoning_text:
179
187
  await reasoning.delta(reasoning_text)
180
188
 
181
- async with self.stream_emitter.text_stream(stream_id) as streamer:
182
- generator = await model.astream_chat(
183
- messages=inputs,
184
- **(
185
- self.step.model.inference_params
186
- if self.step.model.inference_params
187
- else {}
188
- ),
189
- )
190
- async for chat_response in generator:
189
+ # Emit text delta
191
190
  chat_text = chat_response.delta
192
- if chat_text.strip() != "":
193
- await streamer.delta(chat_response.delta)
191
+ if chat_text is not None and chat_text.strip() != "":
192
+ await streamer.delta(chat_text)
193
+
194
194
  # Get the final result
195
195
  chat_result = chat_response
196
196
  else:
@@ -51,9 +51,7 @@ class PromptTemplateExecutor(StepExecutor):
51
51
  input_map = {}
52
52
  for var in self.step.inputs:
53
53
  if var.id in format_args:
54
- value = message.variables.get(var.id)
55
- if value is not None:
56
- input_map[var.id] = value
54
+ input_map[var.id] = message.get_variable(var.id)
57
55
 
58
56
  missing = format_args - input_map.keys()
59
57
  if missing:
@@ -42,8 +42,12 @@ class SQLSourceExecutor(StepExecutor):
42
42
  connect_args = {}
43
43
  if self.step.auth:
44
44
  with auth(self.step.auth, self._secret_manager) as creds:
45
- if isinstance(creds, boto3.Session):
46
- connect_args["session"] = creds
45
+ # For AWS auth, create a boto3 session from credentials
46
+ from qtype.interpreter.auth.aws import AWSCredentials
47
+
48
+ if isinstance(creds, AWSCredentials):
49
+ session = boto3.Session(**creds.as_kwargs())
50
+ connect_args["session"] = session
47
51
  engine = create_engine(connection_string, connect_args=connect_args)
48
52
 
49
53
  output_columns = {output.id for output in self.step.outputs}
qtype/interpreter/flow.py CHANGED
@@ -17,7 +17,7 @@ from qtype.interpreter.base import factory
17
17
  from qtype.interpreter.base.executor_context import ExecutorContext
18
18
  from qtype.interpreter.logging_progress import LoggingProgressCallback
19
19
  from qtype.interpreter.rich_progress import RichProgressCallback
20
- from qtype.interpreter.types import FlowMessage, ProgressCallback
20
+ from qtype.interpreter.types import FlowMessage, ProgressCallback, Session
21
21
  from qtype.semantic.model import Flow
22
22
 
23
23
  logger = logging.getLogger(__name__)
@@ -106,6 +106,16 @@ async def run_flow(
106
106
  initial = [initial]
107
107
 
108
108
  if isinstance(initial, list):
109
+ # Handle empty list by creating a single empty message
110
+ if len(initial) == 0:
111
+ session_id = kwargs.get("session_id", "default")
112
+ initial = [
113
+ FlowMessage(
114
+ session=Session(session_id=session_id),
115
+ variables={},
116
+ )
117
+ ]
118
+
109
119
  span.set_attribute("flow.input_count", len(initial))
110
120
 
111
121
  # convert to async iterator
@@ -14,7 +14,7 @@ from pydantic import create_model
14
14
  from qtype.base.types import PrimitiveTypeEnum
15
15
  from qtype.dsl.model import ListType
16
16
  from qtype.dsl.types import PRIMITIVE_TO_PYTHON_TYPE
17
- from qtype.semantic.model import APITool, PythonFunctionTool, ToolParameter
17
+ from qtype.semantic.model import APITool, PythonFunctionTool, Variable
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
@@ -29,9 +29,9 @@ class FunctionToolHelper:
29
29
 
30
30
  @staticmethod
31
31
  def _qtype_type_to_python_type(
32
- param: ToolParameter,
32
+ param: Variable,
33
33
  ) -> type:
34
- """Convert QType ToolParameter type to Python type for Pydantic.
34
+ """Convert QType Variable type to Python type for Pydantic.
35
35
 
36
36
  The param.type has already been resolved during semantic model
37
37
  creation, so we just need to convert it to the appropriate Python
@@ -42,7 +42,7 @@ class FunctionToolHelper:
42
42
  - Unknown → str
43
43
 
44
44
  Args:
45
- param: The QType ToolParameter to convert.
45
+ param: The QType Variable to convert.
46
46
 
47
47
  Returns:
48
48
  Python type suitable for Pydantic field annotation.
@@ -55,7 +55,8 @@ class FunctionToolHelper:
55
55
  if isinstance(param.type, ListType):
56
56
  # Create a mock parameter with the element type to recursively
57
57
  # resolve it
58
- element_param = ToolParameter(
58
+ element_param = Variable(
59
+ id="temp",
59
60
  type=param.type.element_type,
60
61
  optional=False,
61
62
  )
@@ -74,13 +75,13 @@ class FunctionToolHelper:
74
75
  @staticmethod
75
76
  def _create_fn_schema(
76
77
  tool_name: str,
77
- inputs: dict[str, ToolParameter],
78
+ inputs: list[Variable],
78
79
  ) -> type[BaseModel] | None:
79
80
  """Create a Pydantic model from QType tool input parameters.
80
81
 
81
82
  Args:
82
83
  tool_name: Name of the tool (used for model name).
83
- inputs: Dictionary of input parameter names to ToolParameter.
84
+ inputs: List of input Variables.
84
85
 
85
86
  Returns:
86
87
  Pydantic BaseModel class representing the tool's input schema.
@@ -91,17 +92,17 @@ class FunctionToolHelper:
91
92
  # Each field is a tuple of (type_annotation, field_info)
92
93
  field_definitions: dict[str, Any] = {}
93
94
 
94
- for param_name, param in inputs.items():
95
+ for param in inputs:
95
96
  python_type = FunctionToolHelper._qtype_type_to_python_type(param)
96
97
 
97
98
  # Create field with optional annotation
98
99
  if param.optional:
99
- field_definitions[param_name] = (
100
+ field_definitions[param.id] = (
100
101
  python_type | None, # type: ignore[valid-type]
101
102
  PydanticField(default=None),
102
103
  )
103
104
  else:
104
- field_definitions[param_name] = (
105
+ field_definitions[param.id] = (
105
106
  python_type,
106
107
  PydanticField(...),
107
108
  )
@@ -1,11 +1,38 @@
1
1
  from typing import Any, Dict, Literal, Optional, Protocol, Union
2
2
 
3
- from pydantic import BaseModel, ConfigDict, Field
3
+ from pydantic import BaseModel, ConfigDict, Field, model_serializer
4
4
 
5
5
  from qtype.base.types import StrictBaseModel
6
6
  from qtype.dsl.domain_types import ChatMessage
7
7
  from qtype.semantic.model import Step
8
8
 
9
+
10
+ class _UnsetType:
11
+ """Sentinel representing an unset variable.
12
+
13
+ Distinguishes between:
14
+ - Variable never mentioned (not in dict)
15
+ - Variable explicitly unset (UNSET value in dict)
16
+ - Variable set to None (None value in dict)
17
+ """
18
+
19
+ _instance = None
20
+
21
+ def __new__(cls):
22
+ if cls._instance is None:
23
+ cls._instance = super().__new__(cls)
24
+ return cls._instance
25
+
26
+ def __repr__(self) -> str:
27
+ return "UNSET"
28
+
29
+ def __bool__(self) -> bool:
30
+ return False
31
+
32
+
33
+ UNSET = _UnsetType()
34
+
35
+
9
36
  # Stream Event Types (Discriminated Union)
10
37
  # These events are emitted by executors during flow execution
11
38
  # and can be converted to Vercel UI chunks for frontend display
@@ -293,8 +320,9 @@ class FlowMessage(BaseModel):
293
320
  """
294
321
 
295
322
  model_config = ConfigDict(
296
- frozen=True
297
- ) # Enforces immutability at the model level
323
+ frozen=True,
324
+ arbitrary_types_allowed=True, # Allow UNSET sentinel
325
+ )
298
326
 
299
327
  session: Session
300
328
  variables: Dict[str, Any] = Field(
@@ -307,6 +335,49 @@ class FlowMessage(BaseModel):
307
335
  """Checks if this state has encountered an error."""
308
336
  return self.error is not None
309
337
 
338
+ def is_set(self, var_id: str) -> bool:
339
+ """Check if a variable is set (not UNSET, may be None)."""
340
+ value = self.variables.get(var_id, UNSET)
341
+ return value is not UNSET
342
+
343
+ def get_variable(self, var_id: str, *, default: Any = UNSET) -> Any:
344
+ """Get variable value, raising if unset and no default provided.
345
+
346
+ Args:
347
+ var_id: Variable identifier
348
+ default: Value to return if variable is unset. If not provided,
349
+ raises ValueError on unset variables.
350
+
351
+ Returns:
352
+ Variable value (may be None if explicitly set to None)
353
+
354
+ Raises:
355
+ ValueError: If variable is unset and no default provided
356
+
357
+ Examples:
358
+ # Required variable - throws if unset
359
+ value = message.get_variable("user_input")
360
+
361
+ # Optional variable - returns None if unset
362
+ value = message.get_variable("optional_field", default=None)
363
+
364
+ # Optional with custom default
365
+ value = message.get_variable("count", default=0)
366
+ """
367
+ value = self.variables.get(var_id, UNSET)
368
+
369
+ if value is UNSET:
370
+ if default is UNSET:
371
+ raise ValueError(
372
+ (
373
+ f"Required variable '{var_id}' is not set. "
374
+ f"Available variables: {list(self.variables.keys())}"
375
+ )
376
+ )
377
+ return default
378
+
379
+ return value
380
+
310
381
  def copy_with_error(self, step_id: str, exc: Exception) -> "FlowMessage":
311
382
  """Returns a copy of this state marked as failed."""
312
383
  return self.model_copy(
@@ -319,15 +390,29 @@ class FlowMessage(BaseModel):
319
390
  }
320
391
  )
321
392
 
322
- # It's useful to have copy-on-write style helpers
323
393
  def copy_with_variables(
324
394
  self, new_variables: dict[str, Any]
325
395
  ) -> "FlowMessage":
396
+ """Create a new FlowMessage with updated variables.
397
+
398
+ Note: Can set variables to UNSET to explicitly mark them as unset.
399
+ """
326
400
  new_vars = self.variables.copy()
327
401
  new_vars.update(new_variables)
328
402
  new_state = self.model_copy(update={"variables": new_vars})
329
403
  return new_state
330
404
 
405
+ @model_serializer
406
+ def serialize_model(self):
407
+ """Custom serialization that excludes UNSET variables."""
408
+ return {
409
+ "session": self.session,
410
+ "variables": {
411
+ k: v for k, v in self.variables.items() if v is not UNSET
412
+ },
413
+ "error": self.error,
414
+ }
415
+
331
416
 
332
417
  class InterpreterError(Exception):
333
418
  """Base exception class for ProtoGen interpreter errors."""
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import uuid
4
- from typing import Any, Type, get_origin
4
+ from typing import Any, Type
5
5
 
6
- from pydantic import BaseModel, Field, TypeAdapter, create_model
6
+ from pydantic import BaseModel, Field, create_model
7
7
 
8
8
  from qtype.dsl.model import ListType, PrimitiveTypeEnum
9
9
  from qtype.dsl.model import Variable as DSLVariable
@@ -138,37 +138,36 @@ def flow_results_to_output_container(
138
138
  return output_container(outputs=outputs, errors=errors)
139
139
 
140
140
 
141
- def instantiate_variable(variable: DSLVariable, value: Any) -> Any:
141
+ def convert_dict_to_typed_variables(
142
+ data: dict[str, Any], variables: list[Variable]
143
+ ) -> dict[str, Any]:
142
144
  """
143
- Unified contract to ensure data matches its QType definition.
144
- Handles CustomTypes, DomainTypes, and Primitives.
145
+ Convert a dictionary of raw values to properly typed variables.
146
+
147
+ Uses Pydantic model validation to convert all values at once based on
148
+ Variable type declarations. This is more efficient than converting each
149
+ field individually.
150
+
151
+ Args:
152
+ data: Dictionary with raw values (e.g., from DataFrame row)
153
+ variables: List of Variable definitions with type information
154
+
155
+ Returns:
156
+ Dictionary with values converted to their declared types
157
+
158
+ Raises:
159
+ ValidationError: If values cannot be converted to declared types
145
160
  """
146
- target_type, _ = _get_variable_type(variable)
161
+ # Create a Pydantic model from the variable definitions
162
+ model_class = create_model(
163
+ "TypedVariables",
164
+ __base__=BaseModel,
165
+ **_fields_from_variables(variables),
166
+ )
147
167
 
148
- # 1. Handle the 'Parameterized Generic' Check (The isinstance fix)
149
- # We check if target_type is a generic (like list[T]) vs a simple class.
150
- origin = get_origin(target_type)
168
+ # Validate and convert the data using Pydantic
169
+ validated = model_class.model_validate(data)
151
170
 
152
- if origin is None:
153
- # It's a simple type (int, RAGChunk, etc.)
154
- if isinstance(value, target_type):
155
- return value
156
- else:
157
- # It's a generic (list[str], etc.).
158
- # We skip the identity check and let TypeAdapter handle it.
159
- pass
160
-
161
- # 2. Handle Pydantic Models (Custom/Domain Types)
162
- if hasattr(target_type, "model_validate"):
163
- return target_type.model_validate(value) # type: ignore[misc]
164
-
165
- # 3. Handle Primitives & Complex Python Types (List, Optional, Union)
166
- try:
167
- # TypeAdapter is the "V2 way" to validate things that aren't
168
- # full Pydantic models (like List[int] or Optional[str])
169
- return TypeAdapter(target_type).validate_python(value)
170
- except Exception:
171
- # Fallback to your original manual cast if TypeAdapter is overkill
172
- if isinstance(target_type, type):
173
- return target_type(value)
174
- raise ValueError(f"Unsupported target type: {target_type}")
171
+ # Return as dict but preserve actual typed instances (not serialized)
172
+ # Use __dict__ to get the actual field values without serialization
173
+ return dict(validated)