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.
- qtype/` +0 -0
- qtype/application/__init__.py +0 -2
- qtype/application/converters/tools_from_api.py +28 -22
- qtype/application/converters/tools_from_module.py +66 -32
- qtype/base/__init__.py +8 -2
- qtype/base/logging.py +0 -17
- qtype/base/resources.py +193 -0
- qtype/cli.py +5 -9
- qtype/commands/generate.py +95 -7
- qtype/commands/run.py +153 -54
- qtype/docs/.pages +8 -0
- {docs → qtype/docs}/Concepts/mental-model-and-philosophy.md +1 -1
- qtype/docs/Contributing/.pages +4 -0
- {docs → qtype/docs}/Contributing/index.md +8 -1
- {docs → qtype/docs}/Gallery/dataflow_pipelines.md +18 -4
- qtype/docs/Gallery/recipe_chatbot.md +103 -0
- qtype/docs/Gallery/recipe_chatbot.mermaid +62 -0
- qtype/docs/Gallery/recipe_chatbot.png +0 -0
- {docs → qtype/docs}/Gallery/research_assistant.md +4 -5
- {docs → qtype/docs}/Gallery/simple_chatbot.md +3 -1
- {docs → qtype/docs}/How To/Authentication/configure_aws_authentication.md +2 -2
- {docs → qtype/docs}/How To/Authentication/use_api_key_authentication.md +2 -2
- {docs → qtype/docs}/How To/Command Line Usage/load_multiple_inputs_from_files.md +24 -9
- {docs → qtype/docs}/How To/Command Line Usage/pass_inputs_on_the_cli.md +7 -4
- {docs → qtype/docs}/How To/Command Line Usage/serve_with_auto_reload.md +3 -2
- {docs → qtype/docs}/How To/Data Processing/adjust_concurrency.md +3 -4
- {docs → qtype/docs}/How To/Data Processing/cache_step_results.md +2 -2
- {docs → qtype/docs}/How To/Data Processing/decode_json_xml.md +1 -1
- {docs → qtype/docs}/How To/Data Processing/explode_collections.md +2 -2
- {docs → qtype/docs}/How To/Data Processing/gather_results.md +4 -4
- qtype/docs/How To/Data Processing/invoke_other_flows.md +71 -0
- qtype/docs/How To/Data Processing/load_data_from_athena.md +49 -0
- qtype/docs/How To/Data Processing/load_documents.md +74 -0
- qtype/docs/How To/Data Processing/read_data_from_files.md +61 -0
- {docs → qtype/docs}/How To/Data Processing/read_sql_databases.md +4 -3
- {docs → qtype/docs}/How To/Data Processing/write_data_to_file.md +1 -2
- {docs → qtype/docs}/How To/Invoke Models/call_large_language_models.md +1 -1
- {docs → qtype/docs}/How To/Invoke Models/create_embeddings.md +1 -1
- {docs → qtype/docs}/How To/Invoke Models/reuse_prompts_with_templates.md +2 -3
- {docs → qtype/docs}/How To/Language Features/include_raw_text_from_other_files.md +2 -1
- {docs → qtype/docs}/How To/Language Features/reference_entities_by_id.md +2 -2
- qtype/docs/How To/Language Features/use_agent_skills.md +29 -0
- {docs → qtype/docs}/How To/Language Features/use_environment_variables.md +2 -1
- qtype/docs/How To/Language Features/use_optional_variables.md +42 -0
- {docs → qtype/docs}/How To/Language Features/use_qtype_mcp.md +4 -4
- {docs → qtype/docs}/How To/Observability & Debugging/trace_calls_with_open_telemetry.md +1 -1
- {docs → qtype/docs}/How To/Observability & Debugging/validate_qtype_yaml.md +3 -2
- {docs → qtype/docs}/How To/Observability & Debugging/visualize_application_architecture.md +1 -1
- {docs → qtype/docs}/How To/Qtype Server/serve_flows_as_apis.md +3 -3
- {docs → qtype/docs}/How To/Qtype Server/serve_flows_as_ui.md +2 -3
- {docs → qtype/docs}/How To/Qtype Server/use_conversational_interfaces.md +1 -4
- {docs → qtype/docs}/How To/Qtype Server/use_variables_with_ui_hints.md +3 -2
- {docs → qtype/docs}/How To/Tools & Integration/bind_tool_inputs_and_outputs.md +1 -2
- {docs → qtype/docs}/How To/Tools & Integration/create_tools_from_openapi_specifications.md +10 -14
- {docs → qtype/docs}/How To/Tools & Integration/create_tools_from_python_modules.md +5 -8
- {docs → qtype/docs}/Reference/cli.md +16 -17
- qtype/docs/Tutorials/.pages +1 -0
- {docs → qtype/docs}/Tutorials/01-first-qtype-application.md +4 -3
- {docs → qtype/docs}/Tutorials/02-conversational-chatbot.md +3 -3
- {docs → qtype/docs}/Tutorials/03-structured-data.md +10 -11
- {docs → qtype/docs}/Tutorials/04-tools-and-function-calling.md +13 -20
- {docs → qtype/docs}/components/APITool.md +1 -1
- qtype/docs/components/Aggregate.md +7 -0
- qtype/docs/components/Collect.md +6 -0
- qtype/docs/components/Construct.md +6 -0
- {docs → qtype/docs}/components/DocumentEmbedder.md +0 -1
- {docs → qtype/docs}/components/DocumentSplitter.md +0 -1
- qtype/docs/components/Explode.md +5 -0
- {docs → qtype/docs}/components/FieldExtractor.md +2 -1
- qtype/docs/components/InvokeFlow.md +8 -0
- qtype/docs/components/InvokeTool.md +8 -0
- {docs → qtype/docs}/components/PrimitiveTypeEnum.md +0 -1
- {docs → qtype/docs}/components/Source.md +0 -1
- {docs → qtype/docs}/components/Step.md +0 -1
- {docs → qtype/docs}/components/Tool.md +2 -2
- {docs → qtype/docs}/components/Variable.md +2 -0
- qtype/docs/legacy_how_tos/.pages +6 -0
- qtype/docs/skills/architect/SKILL.md +188 -0
- qtype/docs/skills/architect/references/cheatsheet.md +198 -0
- qtype/docs/skills/architect/references/patterns.md +29 -0
- qtype/docs/stylesheets/extra.css +27 -0
- qtype/dsl/linker.py +8 -0
- qtype/dsl/model.py +177 -84
- qtype/examples/conversational_ai/simple_chatbot_with_auth.qtype.yaml +48 -0
- qtype/examples/data_processing/athena_query.qtype.yaml +56 -0
- qtype/examples/data_processing/batch_inputs.csv +5 -0
- qtype/examples/data_processing/create_sample_db.py +129 -0
- qtype/examples/data_processing/invoke_other_flows.qtype.yaml +98 -0
- qtype/examples/data_processing/load_documents.qtype.yaml +31 -0
- qtype/examples/data_processing/reviews.db +0 -0
- qtype/examples/data_processing/sample_article.txt +1 -0
- qtype/examples/data_processing/sample_documents.jsonl +5 -0
- qtype/examples/invoke_models/invoke_embedding_aws.qtype.yaml +45 -0
- qtype/examples/language_features/optional_variables.qtype.yaml +32 -0
- qtype/examples/language_features/story_prompt.txt +6 -0
- qtype/examples/legacy/data/customers.csv +6 -0
- qtype/examples/legacy/echo/readme.md +29 -0
- qtype/examples/legacy/qtype_plugin_example.py +51 -0
- qtype/examples/legacy/sample_data.txt +43 -0
- qtype/examples/legacy/vertex/README.md +11 -0
- qtype/examples/rag/recipe_chatbot.qtype.yaml +216 -0
- qtype/examples/research_assistant/tavily.qtype.yaml +216 -0
- {examples → qtype/examples}/tutorials/03_structured_data.qtype.yaml +2 -2
- {examples → qtype/examples}/tutorials/04_tools_and_function_calling.qtype.yaml +5 -5
- qtype/interpreter/auth/aws.py +94 -17
- qtype/interpreter/auth/generic.py +11 -12
- qtype/interpreter/base/secrets.py +4 -2
- qtype/interpreter/base/stream_emitter.py +19 -13
- qtype/interpreter/conversions.py +15 -14
- qtype/interpreter/converters.py +142 -26
- qtype/interpreter/executors/agent_executor.py +2 -3
- qtype/interpreter/executors/aggregate_executor.py +3 -4
- qtype/interpreter/executors/bedrock_reranker_executor.py +17 -28
- qtype/interpreter/executors/construct_executor.py +15 -15
- qtype/interpreter/executors/doc_to_text_executor.py +1 -3
- qtype/interpreter/executors/document_embedder_executor.py +1 -12
- qtype/interpreter/executors/field_extractor_executor.py +13 -12
- qtype/interpreter/executors/file_source_executor.py +18 -31
- qtype/interpreter/executors/invoke_embedding_executor.py +24 -37
- qtype/interpreter/executors/invoke_flow_executor.py +2 -2
- qtype/interpreter/executors/invoke_tool_executor.py +19 -18
- qtype/interpreter/executors/llm_inference_executor.py +18 -18
- qtype/interpreter/executors/prompt_template_executor.py +1 -3
- qtype/interpreter/executors/sql_source_executor.py +6 -2
- qtype/interpreter/flow.py +11 -1
- qtype/interpreter/tools/function_tool_helper.py +11 -10
- qtype/interpreter/types.py +89 -4
- qtype/interpreter/typing.py +31 -32
- qtype/mcp/server.py +194 -86
- {schema → qtype/schema}/qtype.schema.json +77 -79
- qtype/semantic/checker.py +19 -0
- qtype/semantic/generate.py +3 -6
- qtype/semantic/model.py +26 -33
- qtype/semantic/resolver.py +7 -0
- qtype/semantic/visualize.py +18 -6
- {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/METADATA +47 -46
- qtype-0.1.14.dist-info/RECORD +361 -0
- {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/WHEEL +1 -2
- docs/How To/Data Processing/read_data_from_files.md +0 -35
- docs/components/Aggregate.md +0 -8
- docs/components/InvokeFlow.md +0 -8
- docs/components/InvokeTool.md +0 -8
- docs/components/ToolParameter.md +0 -6
- examples/research_assistant/tavily.qtype.yaml +0 -289
- qtype/application/facade.py +0 -177
- qtype-0.1.12.dist-info/RECORD +0 -325
- qtype-0.1.12.dist-info/top_level.txt +0 -1
- {docs → qtype/docs}/Contributing/roadmap.md +0 -0
- {docs → qtype/docs}/Decisions/ADR-001-Chat-vs-Completion-Endpoint-Features.md +0 -0
- {docs → qtype/docs}/Gallery/dataflow_pipelines.mermaid +0 -0
- {docs → qtype/docs}/Gallery/research_assistant.mermaid +0 -0
- {docs → qtype/docs}/Gallery/simple_chatbot.mermaid +0 -0
- {docs → qtype/docs}/How To/Language Features/include_qtype_yaml.md +0 -0
- {docs → qtype/docs}/How To/Observability & Debugging/visualize_example.mermaid +0 -0
- {docs → qtype/docs}/How To/Qtype Server/flow_as_ui.png +0 -0
- {docs → qtype/docs}/Reference/plugins.md +0 -0
- {docs → qtype/docs}/Reference/semantic-validation-rules.md +0 -0
- {docs → qtype/docs}/Tutorials/example_chat.png +0 -0
- {docs → qtype/docs}/Tutorials/index.md +0 -0
- {docs → qtype/docs}/components/APIKeyAuthProvider.md +0 -0
- {docs → qtype/docs}/components/AWSAuthProvider.md +0 -0
- {docs → qtype/docs}/components/AWSSecretManager.md +0 -0
- {docs → qtype/docs}/components/Agent.md +0 -0
- {docs → qtype/docs}/components/AggregateStats.md +0 -0
- {docs → qtype/docs}/components/Application.md +0 -0
- {docs → qtype/docs}/components/AuthorizationProvider.md +0 -0
- {docs → qtype/docs}/components/AuthorizationProviderList.md +0 -0
- {docs → qtype/docs}/components/BearerTokenAuthProvider.md +0 -0
- {docs → qtype/docs}/components/BedrockReranker.md +0 -0
- {docs → qtype/docs}/components/ChatContent.md +0 -0
- {docs → qtype/docs}/components/ChatMessage.md +0 -0
- {docs → qtype/docs}/components/ConstantPath.md +0 -0
- {docs → qtype/docs}/components/CustomType.md +0 -0
- {docs → qtype/docs}/components/Decoder.md +0 -0
- {docs → qtype/docs}/components/DecoderFormat.md +0 -0
- {docs → qtype/docs}/components/DocToTextConverter.md +0 -0
- {docs → qtype/docs}/components/Document.md +0 -0
- {docs → qtype/docs}/components/DocumentIndex.md +0 -0
- {docs → qtype/docs}/components/DocumentSearch.md +0 -0
- {docs → qtype/docs}/components/DocumentSource.md +0 -0
- {docs → qtype/docs}/components/Echo.md +0 -0
- {docs → qtype/docs}/components/Embedding.md +0 -0
- {docs → qtype/docs}/components/EmbeddingModel.md +0 -0
- {docs → qtype/docs}/components/FileSource.md +0 -0
- {docs → qtype/docs}/components/FileWriter.md +0 -0
- {docs → qtype/docs}/components/Flow.md +0 -0
- {docs → qtype/docs}/components/FlowInterface.md +0 -0
- {docs → qtype/docs}/components/Index.md +0 -0
- {docs → qtype/docs}/components/IndexUpsert.md +0 -0
- {docs → qtype/docs}/components/InvokeEmbedding.md +0 -0
- {docs → qtype/docs}/components/LLMInference.md +0 -0
- {docs → qtype/docs}/components/ListType.md +0 -0
- {docs → qtype/docs}/components/Memory.md +0 -0
- {docs → qtype/docs}/components/MessageRole.md +0 -0
- {docs → qtype/docs}/components/Model.md +0 -0
- {docs → qtype/docs}/components/ModelList.md +0 -0
- {docs → qtype/docs}/components/OAuth2AuthProvider.md +0 -0
- {docs → qtype/docs}/components/PromptTemplate.md +0 -0
- {docs → qtype/docs}/components/PythonFunctionTool.md +0 -0
- {docs → qtype/docs}/components/RAGChunk.md +0 -0
- {docs → qtype/docs}/components/RAGDocument.md +0 -0
- {docs → qtype/docs}/components/RAGSearchResult.md +0 -0
- {docs → qtype/docs}/components/Reranker.md +0 -0
- {docs → qtype/docs}/components/SQLSource.md +0 -0
- {docs → qtype/docs}/components/Search.md +0 -0
- {docs → qtype/docs}/components/SearchResult.md +0 -0
- {docs → qtype/docs}/components/SecretManager.md +0 -0
- {docs → qtype/docs}/components/SecretReference.md +0 -0
- {docs → qtype/docs}/components/TelemetrySink.md +0 -0
- {docs → qtype/docs}/components/ToolList.md +0 -0
- {docs → qtype/docs}/components/TypeList.md +0 -0
- {docs → qtype/docs}/components/VariableList.md +0 -0
- {docs → qtype/docs}/components/VectorIndex.md +0 -0
- {docs → qtype/docs}/components/VectorSearch.md +0 -0
- {docs → qtype/docs}/components/VertexAuthProvider.md +0 -0
- {docs → qtype/docs}/components/Writer.md +0 -0
- {docs → qtype/docs}/example_ui.png +0 -0
- {docs → qtype/docs}/index.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Configuration/modular-yaml.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Configuration/phoenix_projects.png +0 -0
- {docs → qtype/docs}/legacy_how_tos/Configuration/phoenix_traces.png +0 -0
- {docs → qtype/docs}/legacy_how_tos/Configuration/reference-by-id.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Configuration/telemetry-setup.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Data Types/custom-types.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Data Types/domain-types.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Debugging/visualize-apps.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Tools/api-tools.md +0 -0
- {docs → qtype/docs}/legacy_how_tos/Tools/python-tools.md +0 -0
- {examples → qtype/examples}/authentication/aws_authentication.qtype.yaml +0 -0
- {examples → qtype/examples}/conversational_ai/hello_world_chat.qtype.yaml +0 -0
- {examples → qtype/examples}/conversational_ai/simple_chatbot.qtype.yaml +0 -0
- {examples → qtype/examples}/data_processing/batch_processing.qtype.yaml +0 -0
- {examples → qtype/examples}/data_processing/cache_step_results.qtype.yaml +0 -0
- {examples → qtype/examples}/data_processing/collect_results.qtype.yaml +0 -0
- {examples → qtype/examples}/data_processing/dataflow_pipelines.qtype.yaml +0 -0
- {examples → qtype/examples}/data_processing/decode_json.qtype.yaml +0 -0
- {examples → qtype/examples}/data_processing/explode_items.qtype.yaml +0 -0
- {examples → qtype/examples}/data_processing/read_file.qtype.yaml +0 -0
- {examples → qtype/examples}/invoke_models/create_embeddings.qtype.yaml +0 -0
- {examples → qtype/examples}/invoke_models/simple_llm_call.qtype.yaml +0 -0
- {examples → qtype/examples}/language_features/include_raw.qtype.yaml +0 -0
- {examples → qtype/examples}/language_features/ui_hints.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/data_analysis_with_telemetry.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/hello_world.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/hello_world_chat.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/hello_world_chat_with_telemetry.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/hello_world_chat_with_thinking.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/hello_world_completion.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/hello_world_completion_with_auth.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/bedrock/simple_agent_chat.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/chat_with_langfuse.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/data_processor.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/echo/debug_example.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/echo/prompt.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/echo/test.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/echo/video.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/field_extractor_example.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/multi_flow_example.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/openai/hello_world_chat.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/openai/hello_world_chat_with_telemetry.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/rag.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/time_utilities.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/vertex/hello_world_chat.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/vertex/hello_world_completion.qtype.yaml +0 -0
- {examples → qtype/examples}/legacy/vertex/hello_world_completion_with_auth.qtype.yaml +0 -0
- {examples → qtype/examples}/observability_debugging/trace_with_opentelemetry.qtype.yaml +0 -0
- {examples → qtype/examples}/research_assistant/research_assistant.qtype.yaml +0 -0
- {examples → qtype/examples}/research_assistant/tavily.oas.yaml +0 -0
- {examples → qtype/examples}/tutorials/01_hello_world.qtype.yaml +0 -0
- {examples → qtype/examples}/tutorials/02_conversational_chat.qtype.yaml +0 -0
- {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/entry_points.txt +0 -0
- {qtype-0.1.12.dist-info → qtype-0.1.14.dist-info}/licenses/LICENSE +0 -0
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,
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
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(
|
|
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
|
|
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,37 +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
|
-
return {
|
|
289
|
-
"type": self._serialize_type(self.type),
|
|
290
|
-
"optional": self.optional,
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
356
|
class ListType(BaseModel):
|
|
295
357
|
"""Represents a list type with a specific element type."""
|
|
296
358
|
|
|
@@ -418,11 +480,19 @@ class Construct(Step):
|
|
|
418
480
|
"""A step that converts variables into an instance of a Custom or Domain Type"""
|
|
419
481
|
|
|
420
482
|
type: Literal["Construct"] = "Construct"
|
|
421
|
-
|
|
483
|
+
field_bindings: dict[str, Reference[Variable] | str] = Field(
|
|
422
484
|
...,
|
|
423
|
-
description="Mapping
|
|
485
|
+
description="Mapping from type field names to flow variable names.",
|
|
424
486
|
)
|
|
425
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
|
+
|
|
426
496
|
|
|
427
497
|
class PromptTemplate(Step):
|
|
428
498
|
"""Defines a prompt template with a string format and variable bindings.
|
|
@@ -445,12 +515,12 @@ class Tool(StrictBaseModel, ABC):
|
|
|
445
515
|
description: str = Field(
|
|
446
516
|
..., description="Description of what the tool does."
|
|
447
517
|
)
|
|
448
|
-
inputs:
|
|
449
|
-
default_factory=
|
|
518
|
+
inputs: list[Variable] = Field(
|
|
519
|
+
default_factory=list,
|
|
450
520
|
description="Input parameters required by this tool.",
|
|
451
521
|
)
|
|
452
|
-
outputs:
|
|
453
|
-
default_factory=
|
|
522
|
+
outputs: list[Variable] = Field(
|
|
523
|
+
default_factory=list,
|
|
454
524
|
description="Output parameters produced by this tool.",
|
|
455
525
|
)
|
|
456
526
|
|
|
@@ -485,9 +555,9 @@ class APITool(Tool):
|
|
|
485
555
|
default_factory=dict,
|
|
486
556
|
description="Optional HTTP headers to include in the request.",
|
|
487
557
|
)
|
|
488
|
-
parameters:
|
|
489
|
-
default_factory=
|
|
490
|
-
description="
|
|
558
|
+
parameters: list[Variable] = Field(
|
|
559
|
+
default_factory=list,
|
|
560
|
+
description="Path and query parameters for the API call.",
|
|
491
561
|
)
|
|
492
562
|
|
|
493
563
|
|
|
@@ -622,6 +692,9 @@ class FieldExtractor(Step):
|
|
|
622
692
|
The extracted data is used to construct the output variable by passing it
|
|
623
693
|
as keyword arguments to the output type's constructor.
|
|
624
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
|
+
|
|
625
698
|
Example JSONPath expressions:
|
|
626
699
|
- `$.field_name` - Extract a single field
|
|
627
700
|
- `$.items[*]` - Extract all items from a list
|
|
@@ -633,10 +706,6 @@ class FieldExtractor(Step):
|
|
|
633
706
|
...,
|
|
634
707
|
description="JSONPath expression to extract data from the input. Uses jsonpath-ng syntax.",
|
|
635
708
|
)
|
|
636
|
-
fail_on_missing: bool = Field(
|
|
637
|
-
default=True,
|
|
638
|
-
description="Whether to raise an error if the JSONPath matches no data. If False, returns None.",
|
|
639
|
-
)
|
|
640
709
|
|
|
641
710
|
|
|
642
711
|
class InvokeTool(Step, ConcurrentStepMixin):
|
|
@@ -648,33 +717,23 @@ class InvokeTool(Step, ConcurrentStepMixin):
|
|
|
648
717
|
...,
|
|
649
718
|
description="Tool to invoke.",
|
|
650
719
|
)
|
|
651
|
-
input_bindings: dict[str, str] = Field(
|
|
720
|
+
input_bindings: dict[str, Reference[Variable] | str] = Field(
|
|
652
721
|
...,
|
|
653
|
-
description="Mapping from
|
|
722
|
+
description="Mapping from tool parameter names to flow variable names.",
|
|
654
723
|
)
|
|
655
|
-
output_bindings: dict[str, str] = Field(
|
|
724
|
+
output_bindings: dict[str, Reference[Variable] | str] = Field(
|
|
656
725
|
...,
|
|
657
|
-
description="Mapping from
|
|
726
|
+
description="Mapping from tool output names to flow variable names.",
|
|
658
727
|
)
|
|
659
728
|
|
|
660
729
|
@model_validator(mode="after")
|
|
661
730
|
def infer_inputs_outputs_from_bindings(self) -> "InvokeTool":
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
existing_ids = [item for item in existing if isinstance(item, str)]
|
|
669
|
-
inferred_ids = list(bindings.values())
|
|
670
|
-
merged_ids: list[Reference[Variable] | str] = [
|
|
671
|
-
Reference[Variable].model_validate({"$ref": var_id})
|
|
672
|
-
for var_id in dict.fromkeys(existing_ids + inferred_ids)
|
|
673
|
-
]
|
|
674
|
-
return merged_ids
|
|
675
|
-
|
|
676
|
-
self.inputs = _merge_vars(self.inputs, self.input_bindings)
|
|
677
|
-
self.outputs = _merge_vars(self.outputs, self.output_bindings)
|
|
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
|
+
)
|
|
678
737
|
return self
|
|
679
738
|
|
|
680
739
|
|
|
@@ -687,15 +746,25 @@ class InvokeFlow(Step):
|
|
|
687
746
|
...,
|
|
688
747
|
description="Flow to invoke.",
|
|
689
748
|
)
|
|
690
|
-
input_bindings: dict[Reference[Variable]
|
|
749
|
+
input_bindings: dict[str, Reference[Variable] | str] = Field(
|
|
691
750
|
...,
|
|
692
|
-
description="Mapping from variable
|
|
751
|
+
description="Mapping from flow input variable IDs to step variable names.",
|
|
693
752
|
)
|
|
694
|
-
output_bindings: dict[Reference[Variable]
|
|
753
|
+
output_bindings: dict[str, Reference[Variable] | str] = Field(
|
|
695
754
|
...,
|
|
696
|
-
description="Mapping from variable
|
|
755
|
+
description="Mapping from flow output variable IDs to step variable names.",
|
|
697
756
|
)
|
|
698
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
|
+
|
|
699
768
|
|
|
700
769
|
#
|
|
701
770
|
# ---------------- Secret Manager Component ----------------
|
|
@@ -1000,6 +1069,21 @@ class FileSource(Source):
|
|
|
1000
1069
|
description="Reference to a variable with an fsspec-compatible URI to read from, or the uri itself.",
|
|
1001
1070
|
)
|
|
1002
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
|
+
|
|
1003
1087
|
|
|
1004
1088
|
class Writer(Step, BatchableStepMixin):
|
|
1005
1089
|
"""Base class for things that write data in batches."""
|
|
@@ -1020,22 +1104,31 @@ class FileWriter(Writer, BatchableStepMixin):
|
|
|
1020
1104
|
description="Configuration for processing the input stream in batches. If omitted, the step processes items one by one.",
|
|
1021
1105
|
)
|
|
1022
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
|
+
|
|
1023
1122
|
|
|
1024
1123
|
class Aggregate(Step):
|
|
1025
1124
|
"""
|
|
1026
|
-
A
|
|
1027
|
-
|
|
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.
|
|
1028
1128
|
"""
|
|
1029
1129
|
|
|
1030
1130
|
type: Literal["Aggregate"] = "Aggregate"
|
|
1031
1131
|
|
|
1032
|
-
# Outputs are now optional. The user can provide 0, 1, 2, or 3 names.
|
|
1033
|
-
# The order will be: success_count, error_count, total_count
|
|
1034
|
-
outputs: list[Reference[Variable] | str] = Field(
|
|
1035
|
-
default_factory=list,
|
|
1036
|
-
description="References to the variables for the output. There should be one and only one output with type AggregateStats",
|
|
1037
|
-
)
|
|
1038
|
-
|
|
1039
1132
|
|
|
1040
1133
|
#
|
|
1041
1134
|
# ---------------- Retrieval Augmented Generation Components ----------------
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
id: simple_chatbot
|
|
2
|
+
description: A friendly chatbot with conversation memory using AWS Bedrock
|
|
3
|
+
|
|
4
|
+
# AWS Authentication for Bedrock
|
|
5
|
+
auths:
|
|
6
|
+
- type: aws
|
|
7
|
+
id: aws_auth
|
|
8
|
+
profile_name: ${AWS_PROFILE}
|
|
9
|
+
|
|
10
|
+
models:
|
|
11
|
+
- type: Model
|
|
12
|
+
id: nova_lite
|
|
13
|
+
provider: aws-bedrock
|
|
14
|
+
model_id: amazon.nova-lite-v1:0
|
|
15
|
+
inference_params:
|
|
16
|
+
temperature: 0.7
|
|
17
|
+
max_tokens: 512
|
|
18
|
+
auth: aws_auth
|
|
19
|
+
memories:
|
|
20
|
+
- id: conversation_memory
|
|
21
|
+
token_limit: 10000
|
|
22
|
+
flows:
|
|
23
|
+
- type: Flow
|
|
24
|
+
id: chat_flow
|
|
25
|
+
interface:
|
|
26
|
+
type: Conversational
|
|
27
|
+
variables:
|
|
28
|
+
- id: user_message
|
|
29
|
+
type: ChatMessage
|
|
30
|
+
- id: assistant_response
|
|
31
|
+
type: ChatMessage
|
|
32
|
+
inputs:
|
|
33
|
+
- user_message
|
|
34
|
+
outputs:
|
|
35
|
+
- assistant_response
|
|
36
|
+
steps:
|
|
37
|
+
- id: generate_response
|
|
38
|
+
type: LLMInference
|
|
39
|
+
model: nova_lite
|
|
40
|
+
system_message: |
|
|
41
|
+
You are a friendly and helpful chatbot. You have a warm, conversational
|
|
42
|
+
tone and enjoy helping users with their questions. You remember context
|
|
43
|
+
from previous messages in the conversation.
|
|
44
|
+
memory: conversation_memory
|
|
45
|
+
inputs:
|
|
46
|
+
- user_message
|
|
47
|
+
outputs:
|
|
48
|
+
- assistant_response
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
id: athena-query-example
|
|
2
|
+
description: Query AWS Athena database and process results
|
|
3
|
+
|
|
4
|
+
auths:
|
|
5
|
+
- type: aws
|
|
6
|
+
id: aws_auth
|
|
7
|
+
region: us-east-1
|
|
8
|
+
profile_name: default
|
|
9
|
+
|
|
10
|
+
flows:
|
|
11
|
+
- type: Flow
|
|
12
|
+
id: query-athena
|
|
13
|
+
|
|
14
|
+
variables:
|
|
15
|
+
- id: min_sales
|
|
16
|
+
type: int
|
|
17
|
+
- id: product_id
|
|
18
|
+
type: text
|
|
19
|
+
- id: product_name
|
|
20
|
+
type: text
|
|
21
|
+
- id: total_sales
|
|
22
|
+
type: int
|
|
23
|
+
- id: region
|
|
24
|
+
type: text
|
|
25
|
+
|
|
26
|
+
inputs:
|
|
27
|
+
- min_sales
|
|
28
|
+
|
|
29
|
+
outputs:
|
|
30
|
+
- product_id
|
|
31
|
+
- product_name
|
|
32
|
+
- total_sales
|
|
33
|
+
- region
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- type: SQLSource
|
|
37
|
+
id: load_sales
|
|
38
|
+
connection: "awsathena+rest://:@athena.us-east-1.amazonaws.com:443/sales_db?s3_staging_dir=s3://my-results-bucket/athena-results/&work_group=primary&catalog_name=some_catalog""
|
|
39
|
+
auth: aws_auth
|
|
40
|
+
query: |
|
|
41
|
+
SELECT
|
|
42
|
+
product_id,
|
|
43
|
+
product_name,
|
|
44
|
+
total_sales,
|
|
45
|
+
region
|
|
46
|
+
FROM product_sales
|
|
47
|
+
WHERE total_sales >= :min_sales
|
|
48
|
+
ORDER BY total_sales DESC
|
|
49
|
+
LIMIT 100
|
|
50
|
+
inputs:
|
|
51
|
+
- min_sales
|
|
52
|
+
outputs:
|
|
53
|
+
- product_id
|
|
54
|
+
- product_name
|
|
55
|
+
- total_sales
|
|
56
|
+
- region
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Create a sample SQLite database with product reviews for the example."""
|
|
2
|
+
|
|
3
|
+
import sqlite3
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
# Sample product reviews data
|
|
7
|
+
SAMPLE_REVIEWS = [
|
|
8
|
+
(
|
|
9
|
+
1,
|
|
10
|
+
"Wireless Headphones",
|
|
11
|
+
5,
|
|
12
|
+
"Amazing sound quality! The noise cancellation is superb and "
|
|
13
|
+
"battery lasts all day. Highly recommend for music lovers.",
|
|
14
|
+
),
|
|
15
|
+
(
|
|
16
|
+
2,
|
|
17
|
+
"Wireless Headphones",
|
|
18
|
+
2,
|
|
19
|
+
"Disappointed with the build quality. They broke after just 2 "
|
|
20
|
+
"weeks of normal use. Sound is okay but not worth the price.",
|
|
21
|
+
),
|
|
22
|
+
(
|
|
23
|
+
3,
|
|
24
|
+
"Smart Watch",
|
|
25
|
+
4,
|
|
26
|
+
"Great fitness tracker with accurate heart rate monitoring. "
|
|
27
|
+
"Battery life could be better, but overall very satisfied.",
|
|
28
|
+
),
|
|
29
|
+
(
|
|
30
|
+
4,
|
|
31
|
+
"Smart Watch",
|
|
32
|
+
5,
|
|
33
|
+
"Best smartwatch I've owned! Seamless integration with my phone, "
|
|
34
|
+
"tons of useful features, and looks professional.",
|
|
35
|
+
),
|
|
36
|
+
(
|
|
37
|
+
5,
|
|
38
|
+
"Laptop Stand",
|
|
39
|
+
3,
|
|
40
|
+
"Does the job but feels flimsy. The adjustability is limited and "
|
|
41
|
+
"it wobbles a bit. Expected better quality for the price.",
|
|
42
|
+
),
|
|
43
|
+
(
|
|
44
|
+
6,
|
|
45
|
+
"Laptop Stand",
|
|
46
|
+
5,
|
|
47
|
+
"Perfect for my home office setup! Sturdy construction, multiple "
|
|
48
|
+
"height options, and really helps with posture.",
|
|
49
|
+
),
|
|
50
|
+
(
|
|
51
|
+
7,
|
|
52
|
+
"USB-C Hub",
|
|
53
|
+
4,
|
|
54
|
+
"Works well with all my devices. All ports function properly and "
|
|
55
|
+
"data transfer is fast. Gets a bit warm during heavy use.",
|
|
56
|
+
),
|
|
57
|
+
(
|
|
58
|
+
8,
|
|
59
|
+
"USB-C Hub",
|
|
60
|
+
1,
|
|
61
|
+
"Stopped working after a week. One port was DOA and then the whole "
|
|
62
|
+
"hub died. Total waste of money.",
|
|
63
|
+
),
|
|
64
|
+
(
|
|
65
|
+
9,
|
|
66
|
+
"Mechanical Keyboard",
|
|
67
|
+
5,
|
|
68
|
+
"Typing feels incredible! The switches are responsive and the build "
|
|
69
|
+
"quality is excellent. Worth every penny.",
|
|
70
|
+
),
|
|
71
|
+
(
|
|
72
|
+
10,
|
|
73
|
+
"Mechanical Keyboard",
|
|
74
|
+
4,
|
|
75
|
+
"Great keyboard for coding. Switches are a bit loud for an office "
|
|
76
|
+
"environment but the tactile feedback is amazing.",
|
|
77
|
+
),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def create_database(db_path: Path | str) -> None:
|
|
82
|
+
"""Create SQLite database with sample product reviews.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
db_path: Path where the database file should be created
|
|
86
|
+
"""
|
|
87
|
+
db_path = Path(db_path)
|
|
88
|
+
|
|
89
|
+
# Remove existing database if it exists
|
|
90
|
+
if db_path.exists():
|
|
91
|
+
db_path.unlink()
|
|
92
|
+
|
|
93
|
+
# Create database and table
|
|
94
|
+
conn = sqlite3.connect(db_path)
|
|
95
|
+
cursor = conn.cursor()
|
|
96
|
+
|
|
97
|
+
# Create reviews table
|
|
98
|
+
cursor.execute(
|
|
99
|
+
"""
|
|
100
|
+
CREATE TABLE product_reviews (
|
|
101
|
+
review_id INTEGER PRIMARY KEY,
|
|
102
|
+
product_name TEXT NOT NULL,
|
|
103
|
+
rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
|
|
104
|
+
review_text TEXT NOT NULL,
|
|
105
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
106
|
+
)
|
|
107
|
+
"""
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Insert sample data
|
|
111
|
+
cursor.executemany(
|
|
112
|
+
"""
|
|
113
|
+
INSERT INTO product_reviews
|
|
114
|
+
(review_id, product_name, rating, review_text)
|
|
115
|
+
VALUES (?, ?, ?, ?)
|
|
116
|
+
""",
|
|
117
|
+
SAMPLE_REVIEWS,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
conn.commit()
|
|
121
|
+
conn.close()
|
|
122
|
+
|
|
123
|
+
print(f"Created database at {db_path} with {len(SAMPLE_REVIEWS)} reviews")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
# Create database in the data_processing examples directory
|
|
128
|
+
db_path = Path(__file__).parent / "reviews.db"
|
|
129
|
+
create_database(db_path)
|