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,480 @@
1
+ # Working with Types and Structured Data
2
+
3
+ **Time:** 25 minutes
4
+ **Prerequisites:** [Tutorial 1: Your First QType Application](01-first-qtype-application.md)
5
+ **Example:** [`03_structured_data.qtype.yaml`](https://github.com/bazaarvoice/qtype/blob/main/examples/tutorials/03_structured_data.qtype.yaml)
6
+
7
+ **What you'll learn:**
8
+
9
+ * Define custom types for your domain
10
+ * Parse LLM JSON responses into structured data
11
+ * Build typed objects from extracted fields
12
+ * Work with list types
13
+
14
+ **What you'll build:** An application that analyzes product reviews and extracts structured sentiment data including ratings, confidence scores, and key points.
15
+
16
+ ---
17
+
18
+ ## Background: Why Custom Types?
19
+
20
+ So far you've worked with simple `text` types. But real applications need structure:
21
+
22
+ - **Domain models** - Product reviews, user profiles, search results
23
+ - **Validation** - Ensure data has required fields
24
+ - **Type safety** - Catch errors before runtime
25
+ - **Composability** - Build complex types from simpler ones
26
+
27
+ QType lets you define **CustomTypes** to model your domain, just like you'd define classes in Python.
28
+
29
+ ---
30
+
31
+ ## Part 1: Define Your Custom Type (5 minutes)
32
+
33
+ ### Create Your Application File
34
+
35
+ Create `03_structured_data.qtype.yaml`:
36
+
37
+ ```yaml
38
+ id: review_sentiment_analyzer
39
+ description: |
40
+ Analyzes a product review to extract structured sentiment insights.
41
+ Demonstrates custom types and structured data extraction.
42
+ ```
43
+
44
+ ---
45
+
46
+ ### Define a CustomType
47
+
48
+ Before the `models:` section, add a `types:` section:
49
+
50
+ ```yaml
51
+ types:
52
+ - id: ReviewSentiment
53
+ description: Structured sentiment analysis of a review
54
+ properties:
55
+ sentiment: text
56
+ confidence: float
57
+ key_points: list[text]
58
+ rating: int
59
+ ```
60
+
61
+ **What this means:**
62
+
63
+ **`types:` section** - Where you define custom data structures
64
+
65
+ **`properties:`** - The fields your type contains:
66
+ - `sentiment: text` - A simple text field
67
+ - `confidence: float` - A decimal number (0.0-1.0)
68
+ - `key_points: list[text]` - A list of strings (new!)
69
+ - `rating: int` - An integer (1-5 star rating)
70
+
71
+ **List types** use the syntax `list[element_type]`. Examples:
72
+ - `list[text]` - List of strings
73
+ - `list[int]` - List of integers
74
+ - `list[ReviewSentiment]` - List of custom types
75
+
76
+ ---
77
+
78
+ ### Add Model Configuration
79
+
80
+ ```yaml
81
+ models:
82
+ - type: Model
83
+ id: analyzer_model
84
+ provider: aws-bedrock
85
+ model_id: amazon.nova-lite-v1:0
86
+ inference_params:
87
+ temperature: 0.7
88
+ max_tokens: 512
89
+ ```
90
+
91
+ **Check your work:**
92
+
93
+ ```bash
94
+ qtype validate 03_structured_data.qtype.yaml
95
+ ```
96
+
97
+ Should pass ✅
98
+
99
+ ---
100
+
101
+ ## Part 2: Build the Analysis Flow (10 minutes)
102
+
103
+ ### Define Flow Variables
104
+
105
+ Add the flow structure:
106
+
107
+ ```yaml
108
+ flows:
109
+ - id: analyze_review
110
+ description: Analyzes a single review and extracts structured sentiment
111
+ inputs:
112
+ - review_text
113
+ outputs:
114
+ - result
115
+
116
+ variables:
117
+ - id: review_text
118
+ type: text
119
+ - id: raw_llm_response
120
+ type: text
121
+ - id: llm_response
122
+ type: text
123
+ - id: sentiment
124
+ type: text
125
+ - id: confidence
126
+ type: float
127
+ - id: key_points
128
+ type: list[text]
129
+ - id: rating
130
+ type: int
131
+ - id: result
132
+ type: ReviewSentiment
133
+ ```
134
+
135
+ **What's new:**
136
+
137
+ **Multiple variable types** - Notice we have:
138
+ - Simple types (`text`, `float`, `int`)
139
+ - List type (`list[text]`)
140
+ - Custom type (`ReviewSentiment`)
141
+
142
+ **Why so many variables?** Each step transforms data from one form to another. This explicit data flow makes debugging easier and documents how information moves through your application.
143
+
144
+ ---
145
+
146
+ ### Step 1: Create the Analysis Prompt
147
+
148
+ Add the first step under `steps:`:
149
+
150
+ ```yaml
151
+ steps:
152
+ # Step 1: Create analysis prompt
153
+ - id: analysis_prompt
154
+ type: PromptTemplate
155
+ template: |
156
+ Analyze this product review and extract structured information.
157
+
158
+ Review: {{review_text}}
159
+
160
+ Respond with ONLY valid JSON, no other text or markdown. Use this exact structure:
161
+ {{{{
162
+ "sentiment": "positive|negative|neutral|mixed",
163
+ "confidence": 0.95,
164
+ "key_points": ["point 1", "point 2"],
165
+ "rating": 4
166
+ }}}}
167
+
168
+ Where:
169
+ - sentiment: overall sentiment (positive/negative/neutral/mixed)
170
+ - confidence: your confidence score (0.0-1.0)
171
+ - key_points: 2-3 main points from the review
172
+ - rating: estimated star rating 1-5 based on the tone
173
+
174
+ Return ONLY the JSON object, nothing else.
175
+ inputs:
176
+ - review_text
177
+ outputs:
178
+ - raw_llm_response
179
+ ```
180
+
181
+ **Key technique - Escaping braces:**
182
+
183
+ Notice `{{` and `}}` in the template? QType uses Python's `.format()` method where `{variable}` is a placeholder. To include literal curly braces in the output, you must double them:
184
+ - `{{` → outputs `{`
185
+ - `}}` → outputs `}`
186
+
187
+ So to output the JSON structure, we use `{{ ... }}` which renders as `{ ... }` in the actual prompt.
188
+
189
+ **Why "ONLY valid JSON"?** LLMs often add explanatory text or wrap JSON in markdown code fences. Being explicit reduces these issues.
190
+
191
+ ---
192
+
193
+ ### Step 2: Run LLM Inference
194
+
195
+ ```yaml
196
+ # Step 2: Run LLM inference
197
+ - id: analyze
198
+ type: LLMInference
199
+ model: analyzer_model
200
+ inputs:
201
+ - raw_llm_response
202
+ outputs:
203
+ - llm_response
204
+ ```
205
+
206
+ **LLMInference step** sends the prompt to your model and returns the response as text. Simple and familiar from Tutorial 1.
207
+
208
+ ---
209
+
210
+ ### Step 3: Parse JSON with Decoder
211
+
212
+ Here's the new step type:
213
+
214
+ ```yaml
215
+ # Step 3: Parse the JSON response and build the ReviewSentiment object
216
+ # Decoder converts the JSON string into structured data
217
+ - id: parse_and_build
218
+ type: Decoder
219
+ format: json
220
+ inputs:
221
+ - llm_response
222
+ outputs:
223
+ - sentiment
224
+ - confidence
225
+ - key_points
226
+ - rating
227
+ ```
228
+
229
+ **What Decoder does:**
230
+
231
+ **`format: json`** - Tells QType to parse as JSON (also supports `xml`)
232
+
233
+ **Multiple outputs** - Each output name must match a field in the JSON:
234
+ ```json
235
+ {
236
+ "sentiment": "positive", ← goes to sentiment variable
237
+ "confidence": 0.95, ← goes to confidence variable
238
+ "key_points": [...], ← goes to key_points variable
239
+ "rating": 4 ← goes to rating variable
240
+ }
241
+ ```
242
+
243
+ **Smart parsing:**
244
+ - Automatically strips markdown code fences (````json`)
245
+ - Validates JSON syntax
246
+ - Maps JSON types to QType types (string→text, number→float/int, array→list)
247
+ - Raises clear errors if fields are missing or malformed
248
+
249
+ **Check your work:**
250
+
251
+ ```bash
252
+ qtype validate 03_structured_data.qtype.yaml
253
+ ```
254
+
255
+ ---
256
+
257
+ ### Step 4: Construct the Typed Object
258
+
259
+ Final step - convert individual fields into your custom type:
260
+
261
+ ```yaml
262
+ # Step 4: Construct a ReviewSentiment object
263
+ # Construct builds typed objects from the decoded fields
264
+ - id: build_result
265
+ type: Construct
266
+ output_type: ReviewSentiment
267
+ field_bindings:
268
+ sentiment: sentiment
269
+ confidence: confidence
270
+ key_points: key_points
271
+ rating: rating
272
+ inputs:
273
+ - sentiment
274
+ - confidence
275
+ - key_points
276
+ - rating
277
+ outputs:
278
+ - result
279
+ ```
280
+
281
+ **What Construct does:**
282
+
283
+ **`output_type: ReviewSentiment`** - Specifies which custom type to build
284
+
285
+ **`field_bindings:`** - Maps type field names to input variables:
286
+ ```yaml
287
+ field_bindings:
288
+ <property_name>: <variable_name>
289
+ ```
290
+
291
+ In this case, names match (`sentiment: sentiment`), but you could use different names:
292
+ ```yaml
293
+ field_bindings:
294
+ sentiment: analyzed_sentiment # Maps analyzed_sentiment variable to sentiment property
295
+ ```
296
+
297
+ **Why Construct?** It validates that:
298
+ - All required properties are provided
299
+ - Types match (float for confidence, int for rating, etc.)
300
+ - The result is a valid `ReviewSentiment` instance
301
+
302
+ This catches errors early rather than failing later in your application.
303
+
304
+ **Final validation:**
305
+
306
+ ```bash
307
+ qtype validate 03_structured_data.qtype.yaml
308
+ ```
309
+
310
+ Should pass ✅
311
+
312
+ ---
313
+
314
+ ## Part 3: Test Your Application (5 minutes)
315
+
316
+ ### Run It!
317
+
318
+ ```bash
319
+ qtype run -i '{"review_text":"These headphones are amazing! Great sound quality and super comfortable. Battery lasts all day."}' 03_structured_data.qtype.yaml
320
+ ```
321
+
322
+ **Expected output:**
323
+
324
+ ```json
325
+ {
326
+ "result": {
327
+ "sentiment": "positive",
328
+ "confidence": 0.95,
329
+ "key_points": [
330
+ "Great sound quality",
331
+ "Super comfortable",
332
+ "Long battery life"
333
+ ],
334
+ "rating": 5
335
+ }
336
+ }
337
+ ```
338
+
339
+ ---
340
+
341
+ ### Try Different Reviews
342
+
343
+ ```bash
344
+ # Negative review
345
+ qtype run -i '{"review_text":"Terrible product. Broke after one week and customer service was unhelpful."}' 03_structured_data.qtype.yaml
346
+
347
+ # Mixed review
348
+ qtype run -i '{"review_text":"Good sound but uncomfortable after an hour. Battery is okay but not great."}' 03_structured_data.qtype.yaml
349
+ ```
350
+
351
+ Notice how the LLM adapts its analysis while maintaining the structured format!
352
+
353
+ ---
354
+
355
+ ## Part 4: Understanding the Data Flow (5 minutes)
356
+
357
+ ### The Complete Pipeline
358
+
359
+ Here's what happens when you run the application:
360
+
361
+ ```
362
+ 1. User Input (text)
363
+ "These headphones are amazing!"
364
+
365
+
366
+ 2. PromptTemplate
367
+ Creates prompt with JSON format instructions
368
+
369
+
370
+ 3. LLMInference
371
+ Sends to model → Returns JSON string
372
+
373
+
374
+ 4. Decoder
375
+ Parses JSON string → Extracts individual fields
376
+ {sentiment: "positive", confidence: 0.95, ...}
377
+
378
+
379
+ 5. Construct
380
+ Builds ReviewSentiment object from fields
381
+ ReviewSentiment(sentiment="positive", confidence=0.95, ...)
382
+
383
+
384
+ 6. Output (ReviewSentiment)
385
+ Validated, typed data ready for downstream use
386
+ ```
387
+
388
+ **Key insight:** Each step has a single, focused responsibility:
389
+ - **PromptTemplate** - Format instructions
390
+ - **LLMInference** - Get AI response
391
+ - **Decoder** - Parse structured data
392
+ - **Construct** - Validate and type
393
+
394
+ This separation makes each step testable and reusable.
395
+
396
+ **Note:** You could simplify this by having the LLM return `{"result": {...}}` and using Decoder to output directly to a `ReviewSentiment` variable, skipping the Construct step. However, this tutorial demonstrates both steps separately so you understand when to use each:
397
+ - **Decoder** - When you need to parse text and extract individual fields
398
+ - **Construct** - When you need to build typed objects from already-extracted data
399
+
400
+ In practice, use the approach that best fits your use case.
401
+
402
+ ---
403
+
404
+ ### Error Handling
405
+
406
+ What happens if the LLM returns invalid JSON?
407
+
408
+ **Decoder will fail** with a clear error:
409
+ ```
410
+ Invalid JSON input: Expecting ',' delimiter: line 2 column 5 (char 45)
411
+ ```
412
+
413
+ **What if a field is missing?**
414
+ ```
415
+ Output variable 'confidence' not found in decoded result
416
+ ```
417
+
418
+ **What if a type is wrong?**
419
+ ```
420
+ Cannot construct ReviewSentiment: field 'rating' expects int, got str
421
+ ```
422
+
423
+ These explicit errors help you debug issues quickly. In production, you might add retry logic or fallback values.
424
+
425
+ ---
426
+
427
+ ## What You've Learned
428
+
429
+ Congratulations! You've mastered:
430
+
431
+ ✅ **CustomType definition** - Modeling your domain with structured types
432
+ ✅ **List types** - Working with `list[text]` and other collections
433
+ ✅ **Decoder step** - Parsing JSON into individual typed fields
434
+ ✅ **Construct step** - Building validated custom type instances
435
+ ✅ **Field Bindings** - Connecting variables to type properties
436
+ ✅ **Type safety** - Catching errors early with validation
437
+
438
+ ---
439
+
440
+ ## Next Steps
441
+
442
+ **Reference the complete example:**
443
+
444
+ - [`03_structured_data.qtype.yaml`](https://github.com/bazaarvoice/qtype/blob/main/examples/tutorials/03_structured_data.qtype.yaml) - Full working example
445
+
446
+ **Learn more:**
447
+
448
+ - [Tutorial: Tools and Function Calling](04-tools-and-function-calling.md)
449
+ - [CustomType Reference](../components/CustomType.md)
450
+ - [Decoder Reference](../components/Decoder.md)
451
+
452
+ ---
453
+
454
+ ## Common Questions
455
+
456
+ **Q: Can I nest custom types?**
457
+ A: Yes! A CustomType property can be another CustomType:
458
+ ```yaml
459
+ types:
460
+ - id: Address
461
+ properties:
462
+ street: text
463
+ city: text
464
+ - id: User
465
+ properties:
466
+ name: text
467
+ address: Address # Nested custom type
468
+ ```
469
+
470
+ **Q: What if the LLM returns extra fields not in my type?**
471
+ A: Extra fields are ignored. Decoder only extracts the fields you've specified in `outputs:`.
472
+
473
+ **Q: Can Decoder output the entire JSON as one variable?**
474
+ A: Not directly. Decoder maps JSON fields to individual outputs. If you need the whole JSON, use `type: any` in your variable and skip Decoder.
475
+
476
+ **Q: When should I use Decoder vs FieldExtractor?**
477
+ A: Use **Decoder** when you have a JSON/XML string to parse. Use **FieldExtractor** when you already have structured data and need to extract specific fields using JSONPath (covered in advanced tutorials).
478
+
479
+ **Q: Can I make properties optional?**
480
+ A: Yes! Mark variables as optional using the `?` suffix (e.g., `type: text?`). Optional variables can be unset, `None`, or have a value. This is useful when extracting fields that may not always be present. See [Use Optional Variables](../How%20To/Language%20Features/use_optional_variables.md).