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/`
ADDED
|
File without changes
|
qtype/application/__init__.py
CHANGED
|
@@ -26,7 +26,7 @@ from qtype.dsl.model import (
|
|
|
26
26
|
BearerTokenAuthProvider,
|
|
27
27
|
CustomType,
|
|
28
28
|
OAuth2AuthProvider,
|
|
29
|
-
|
|
29
|
+
Variable,
|
|
30
30
|
VariableType,
|
|
31
31
|
)
|
|
32
32
|
|
|
@@ -202,9 +202,9 @@ def create_tool_parameters_from_body(
|
|
|
202
202
|
existing_custom_types: dict[str, CustomType],
|
|
203
203
|
schema_name_map: dict[int, str],
|
|
204
204
|
default_param_name: str,
|
|
205
|
-
) ->
|
|
205
|
+
) -> list[Variable]:
|
|
206
206
|
"""
|
|
207
|
-
Convert an OpenAPI Response or RequestBody to a
|
|
207
|
+
Convert an OpenAPI Response or RequestBody to a list of Variables.
|
|
208
208
|
|
|
209
209
|
If the body has only one content type with an Object schema, flatten its properties
|
|
210
210
|
to individual parameters. Otherwise, create a single parameter with the body type.
|
|
@@ -216,18 +216,18 @@ def create_tool_parameters_from_body(
|
|
|
216
216
|
default_param_name: Name to use for non-flattened parameter
|
|
217
217
|
|
|
218
218
|
Returns:
|
|
219
|
-
|
|
219
|
+
List of Variable objects
|
|
220
220
|
"""
|
|
221
221
|
# Check if we have content to analyze
|
|
222
222
|
if not hasattr(oas, "content") or not oas.content:
|
|
223
|
-
return
|
|
223
|
+
return []
|
|
224
224
|
|
|
225
225
|
content = oas.content[0]
|
|
226
226
|
input_type = to_variable_type(
|
|
227
227
|
content, existing_custom_types, schema_name_map
|
|
228
228
|
)
|
|
229
229
|
|
|
230
|
-
# Convert CustomType to string ID for
|
|
230
|
+
# Convert CustomType to string ID for Variable
|
|
231
231
|
input_type_value = (
|
|
232
232
|
input_type.id if isinstance(input_type, CustomType) else input_type
|
|
233
233
|
)
|
|
@@ -240,7 +240,7 @@ def create_tool_parameters_from_body(
|
|
|
240
240
|
custom_type = existing_custom_types[input_type.id]
|
|
241
241
|
|
|
242
242
|
# Flatten the custom type properties to individual parameters
|
|
243
|
-
flattened_parameters =
|
|
243
|
+
flattened_parameters = []
|
|
244
244
|
for prop_name, prop_type_str in custom_type.properties.items():
|
|
245
245
|
# Check if the property is optional (has '?' suffix)
|
|
246
246
|
is_optional = prop_type_str.endswith("?")
|
|
@@ -248,8 +248,10 @@ def create_tool_parameters_from_body(
|
|
|
248
248
|
prop_type_str.rstrip("?") if is_optional else prop_type_str
|
|
249
249
|
)
|
|
250
250
|
|
|
251
|
-
flattened_parameters
|
|
252
|
-
|
|
251
|
+
flattened_parameters.append(
|
|
252
|
+
Variable.model_construct(
|
|
253
|
+
id=prop_name, type=clean_type, optional=is_optional
|
|
254
|
+
)
|
|
253
255
|
)
|
|
254
256
|
|
|
255
257
|
# remove the type from existing_custom_types to avoid confusion
|
|
@@ -258,11 +260,11 @@ def create_tool_parameters_from_body(
|
|
|
258
260
|
return flattened_parameters
|
|
259
261
|
|
|
260
262
|
# If not flattening, create a single parameter (e.g., for simple types or arrays)
|
|
261
|
-
return
|
|
262
|
-
|
|
263
|
-
type=input_type_value, optional=False
|
|
263
|
+
return [
|
|
264
|
+
Variable.model_construct(
|
|
265
|
+
id=default_param_name, type=input_type_value, optional=False
|
|
264
266
|
)
|
|
265
|
-
|
|
267
|
+
]
|
|
266
268
|
|
|
267
269
|
|
|
268
270
|
def to_api_tool(
|
|
@@ -297,7 +299,7 @@ def to_api_tool(
|
|
|
297
299
|
).replace("\n", " ")
|
|
298
300
|
|
|
299
301
|
# Process inputs from request body and parameters
|
|
300
|
-
inputs =
|
|
302
|
+
inputs = []
|
|
301
303
|
if operation.request_body and operation.request_body.content:
|
|
302
304
|
# Create input parameters from request body using the new function
|
|
303
305
|
input_params = create_tool_parameters_from_body(
|
|
@@ -306,27 +308,31 @@ def to_api_tool(
|
|
|
306
308
|
schema_name_map,
|
|
307
309
|
default_param_name="request",
|
|
308
310
|
)
|
|
309
|
-
inputs.
|
|
311
|
+
inputs.extend(input_params)
|
|
310
312
|
|
|
311
313
|
# Add path and query parameters as inputs
|
|
312
|
-
parameters =
|
|
314
|
+
parameters = []
|
|
313
315
|
for param in operation.parameters:
|
|
314
316
|
if param.schema:
|
|
315
317
|
param_type = _schema_to_qtype_type(
|
|
316
318
|
param.schema, existing_custom_types, schema_name_map
|
|
317
319
|
)
|
|
318
|
-
# Convert to appropriate type for
|
|
320
|
+
# Convert to appropriate type for Variable
|
|
319
321
|
param_type_value = (
|
|
320
322
|
param_type.id
|
|
321
323
|
if isinstance(param_type, CustomType)
|
|
322
324
|
else param_type
|
|
323
325
|
)
|
|
324
|
-
parameters
|
|
325
|
-
|
|
326
|
+
parameters.append(
|
|
327
|
+
Variable.model_construct(
|
|
328
|
+
id=param.name,
|
|
329
|
+
type=param_type_value,
|
|
330
|
+
optional=not param.required,
|
|
331
|
+
)
|
|
326
332
|
)
|
|
327
333
|
|
|
328
334
|
# Process outputs from responses
|
|
329
|
-
outputs =
|
|
335
|
+
outputs = []
|
|
330
336
|
# Find the success response (200-299 status codes) or default response
|
|
331
337
|
success_response = next(
|
|
332
338
|
(r for r in operation.responses if r.code and 200 <= r.code < 300),
|
|
@@ -339,9 +345,9 @@ def to_api_tool(
|
|
|
339
345
|
success_response,
|
|
340
346
|
existing_custom_types,
|
|
341
347
|
schema_name_map,
|
|
342
|
-
default_param_name="
|
|
348
|
+
default_param_name=f"{tool_id}_response",
|
|
343
349
|
)
|
|
344
|
-
outputs.
|
|
350
|
+
outputs.extend(output_params)
|
|
345
351
|
|
|
346
352
|
return APITool(
|
|
347
353
|
id=tool_id,
|
|
@@ -10,7 +10,7 @@ from qtype.dsl.model import (
|
|
|
10
10
|
CustomType,
|
|
11
11
|
ListType,
|
|
12
12
|
PythonFunctionTool,
|
|
13
|
-
|
|
13
|
+
Variable,
|
|
14
14
|
VariableType,
|
|
15
15
|
)
|
|
16
16
|
|
|
@@ -43,14 +43,19 @@ def tools_from_module(
|
|
|
43
43
|
f"No public functions found in module '{module_path}'"
|
|
44
44
|
)
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
# Registry of actual Pydantic classes for validation
|
|
47
|
+
custom_type_registry: dict[str, Type[BaseModel]] = {}
|
|
48
|
+
# CustomType instances for YAML output
|
|
49
|
+
custom_type_models: dict[str, CustomType] = {}
|
|
47
50
|
|
|
48
51
|
# Create Tool instances from functions
|
|
49
52
|
tools = [
|
|
50
|
-
_create_tool_from_function(
|
|
53
|
+
_create_tool_from_function(
|
|
54
|
+
func_name, func_info, custom_type_registry, custom_type_models
|
|
55
|
+
)
|
|
51
56
|
for func_name, func_info in functions.items()
|
|
52
57
|
]
|
|
53
|
-
return (tools, list(
|
|
58
|
+
return (tools, list(custom_type_models.values()))
|
|
54
59
|
except ImportError as e:
|
|
55
60
|
raise ImportError(f"Cannot import module '{module_path}': {e}") from e
|
|
56
61
|
|
|
@@ -116,7 +121,8 @@ def _get_module_functions(
|
|
|
116
121
|
def _create_tool_from_function(
|
|
117
122
|
func_name: str,
|
|
118
123
|
func_info: dict[str, Any],
|
|
119
|
-
|
|
124
|
+
custom_type_registry: dict[str, Type[BaseModel]],
|
|
125
|
+
custom_type_models: dict[str, CustomType],
|
|
120
126
|
) -> PythonFunctionTool:
|
|
121
127
|
"""
|
|
122
128
|
Convert function metadata into a Tool instance.
|
|
@@ -135,29 +141,38 @@ def _create_tool_from_function(
|
|
|
135
141
|
else f"Function {func_name}"
|
|
136
142
|
)
|
|
137
143
|
|
|
138
|
-
# Create input parameters
|
|
139
|
-
inputs =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
# Create input parameters as list of Variables
|
|
145
|
+
inputs = [
|
|
146
|
+
Variable.model_validate(
|
|
147
|
+
{
|
|
148
|
+
"id": p["name"],
|
|
149
|
+
"type": _map_python_type_to_variable_type(
|
|
150
|
+
p["type"], custom_type_registry, custom_type_models
|
|
151
|
+
),
|
|
152
|
+
"optional": p["default"] != inspect.Parameter.empty,
|
|
153
|
+
},
|
|
154
|
+
context={"custom_types": custom_type_registry},
|
|
143
155
|
)
|
|
144
156
|
for p in func_info["parameters"]
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# # quick hack
|
|
148
|
-
# for k, v in inputs.items():
|
|
149
|
-
# if inspect.isclass(v.type) and issubclass(v.type, BaseModel):
|
|
150
|
-
# v.type = str(v.type.__name__)
|
|
157
|
+
]
|
|
151
158
|
|
|
152
159
|
# Create output parameter based on return type
|
|
153
160
|
tool_id = func_info["module"] + "." + func_name
|
|
154
161
|
|
|
155
162
|
output_type = _map_python_type_to_variable_type(
|
|
156
|
-
func_info["return_type"],
|
|
163
|
+
func_info["return_type"], custom_type_registry, custom_type_models
|
|
157
164
|
)
|
|
158
165
|
|
|
159
|
-
outputs =
|
|
160
|
-
|
|
166
|
+
outputs = [
|
|
167
|
+
Variable.model_validate(
|
|
168
|
+
{
|
|
169
|
+
"id": f"{func_name}_result",
|
|
170
|
+
"type": output_type,
|
|
171
|
+
"optional": False,
|
|
172
|
+
},
|
|
173
|
+
context={"custom_types": custom_type_registry},
|
|
174
|
+
)
|
|
175
|
+
]
|
|
161
176
|
|
|
162
177
|
return PythonFunctionTool(
|
|
163
178
|
id=tool_id,
|
|
@@ -172,7 +187,8 @@ def _create_tool_from_function(
|
|
|
172
187
|
|
|
173
188
|
def _pydantic_to_custom_types(
|
|
174
189
|
model_cls: Type[BaseModel],
|
|
175
|
-
|
|
190
|
+
custom_type_registry: dict[str, Type[BaseModel]],
|
|
191
|
+
custom_type_models: dict[str, CustomType],
|
|
176
192
|
) -> str:
|
|
177
193
|
"""
|
|
178
194
|
Converts a Pydantic BaseModel class into a QType CustomType.
|
|
@@ -184,15 +200,20 @@ def _pydantic_to_custom_types(
|
|
|
184
200
|
|
|
185
201
|
Args:
|
|
186
202
|
model_cls: The Pydantic model class to convert.
|
|
203
|
+
custom_type_registry: Registry of actual Pydantic classes for validation
|
|
204
|
+
custom_type_models: Dictionary of CustomType models for YAML output
|
|
187
205
|
|
|
188
206
|
Returns:
|
|
189
|
-
|
|
207
|
+
The model name as a string type reference
|
|
190
208
|
"""
|
|
191
209
|
properties = {}
|
|
192
210
|
model_name = model_cls.__name__
|
|
193
|
-
if model_name in
|
|
211
|
+
if model_name in custom_type_registry:
|
|
194
212
|
return model_name # Already processed
|
|
195
213
|
|
|
214
|
+
# Register the actual class for validation
|
|
215
|
+
custom_type_registry[model_name] = model_cls
|
|
216
|
+
|
|
196
217
|
for field_name, field_info in model_cls.model_fields.items():
|
|
197
218
|
# Use the annotation (the type hint) for the field
|
|
198
219
|
field_type = field_info.annotation
|
|
@@ -202,22 +223,27 @@ def _pydantic_to_custom_types(
|
|
|
202
223
|
)
|
|
203
224
|
elif get_origin(field_type) is Union:
|
|
204
225
|
# Assume the union means it's optional
|
|
226
|
+
# TODO: support proper unions
|
|
205
227
|
field_type = [
|
|
206
228
|
t for t in get_args(field_type) if t is not type(None)
|
|
207
229
|
][0]
|
|
208
|
-
rv = _map_python_type_to_type_str(
|
|
230
|
+
rv = _map_python_type_to_type_str(
|
|
231
|
+
field_type, custom_type_registry, custom_type_models
|
|
232
|
+
)
|
|
209
233
|
properties[field_name] = f"{rv}?"
|
|
210
234
|
elif get_origin(field_type) is list:
|
|
211
235
|
inner_type = get_args(field_type)[0]
|
|
212
|
-
rv = _map_python_type_to_type_str(
|
|
236
|
+
rv = _map_python_type_to_type_str(
|
|
237
|
+
inner_type, custom_type_registry, custom_type_models
|
|
238
|
+
)
|
|
213
239
|
properties[field_name] = f"list[{rv}]"
|
|
214
240
|
else:
|
|
215
241
|
properties[field_name] = _map_python_type_to_type_str(
|
|
216
|
-
field_type,
|
|
242
|
+
field_type, custom_type_registry, custom_type_models
|
|
217
243
|
)
|
|
218
244
|
|
|
219
|
-
# Add the
|
|
220
|
-
|
|
245
|
+
# Add the CustomType model for YAML output
|
|
246
|
+
custom_type_models[model_name] = CustomType(
|
|
221
247
|
id=model_name,
|
|
222
248
|
properties=properties,
|
|
223
249
|
description=model_cls.__doc__ or f"Custom type for {model_name}",
|
|
@@ -227,7 +253,8 @@ def _pydantic_to_custom_types(
|
|
|
227
253
|
|
|
228
254
|
def _map_python_type_to_variable_type(
|
|
229
255
|
python_type: Any,
|
|
230
|
-
|
|
256
|
+
custom_type_registry: dict[str, Type[BaseModel]],
|
|
257
|
+
custom_type_models: dict[str, CustomType],
|
|
231
258
|
) -> str | VariableType:
|
|
232
259
|
"""
|
|
233
260
|
Map Python type annotations to QType VariableType.
|
|
@@ -248,7 +275,9 @@ def _map_python_type_to_variable_type(
|
|
|
248
275
|
element_type_annotation = args[0]
|
|
249
276
|
# Recursively map the element type
|
|
250
277
|
element_type = _map_python_type_to_variable_type(
|
|
251
|
-
element_type_annotation,
|
|
278
|
+
element_type_annotation,
|
|
279
|
+
custom_type_registry,
|
|
280
|
+
custom_type_models,
|
|
252
281
|
)
|
|
253
282
|
# Support lists of both primitive types and custom types
|
|
254
283
|
if isinstance(element_type, PrimitiveTypeEnum):
|
|
@@ -281,7 +310,9 @@ def _map_python_type_to_variable_type(
|
|
|
281
310
|
return python_type.__name__
|
|
282
311
|
elif inspect.isclass(python_type) and issubclass(python_type, BaseModel):
|
|
283
312
|
# If it's a Pydantic model, create or retrieve its CustomType definition
|
|
284
|
-
return _pydantic_to_custom_types(
|
|
313
|
+
return _pydantic_to_custom_types(
|
|
314
|
+
python_type, custom_type_registry, custom_type_models
|
|
315
|
+
)
|
|
285
316
|
raise ValueError(
|
|
286
317
|
f"Unsupported Python type '{python_type}' for VariableType mapping"
|
|
287
318
|
)
|
|
@@ -289,9 +320,12 @@ def _map_python_type_to_variable_type(
|
|
|
289
320
|
|
|
290
321
|
def _map_python_type_to_type_str(
|
|
291
322
|
python_type: Any,
|
|
292
|
-
|
|
323
|
+
custom_type_registry: dict[str, Type[BaseModel]],
|
|
324
|
+
custom_type_models: dict[str, CustomType],
|
|
293
325
|
) -> str:
|
|
294
|
-
var_type = _map_python_type_to_variable_type(
|
|
326
|
+
var_type = _map_python_type_to_variable_type(
|
|
327
|
+
python_type, custom_type_registry, custom_type_models
|
|
328
|
+
)
|
|
295
329
|
if isinstance(var_type, PrimitiveTypeEnum):
|
|
296
330
|
return var_type.value
|
|
297
331
|
elif inspect.isclass(python_type):
|
qtype/base/__init__.py
CHANGED
|
@@ -3,12 +3,18 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from .exceptions import QTypeError, ValidationError
|
|
6
|
-
from .
|
|
6
|
+
from .resources import (
|
|
7
|
+
ResourceDirectory,
|
|
8
|
+
get_docs_resource,
|
|
9
|
+
get_examples_resource,
|
|
10
|
+
)
|
|
7
11
|
from .types import JSONValue
|
|
8
12
|
|
|
9
13
|
__all__ = [
|
|
10
14
|
"QTypeError",
|
|
11
15
|
"ValidationError",
|
|
12
|
-
"get_logger",
|
|
13
16
|
"JSONValue",
|
|
17
|
+
"ResourceDirectory",
|
|
18
|
+
"get_docs_resource",
|
|
19
|
+
"get_examples_resource",
|
|
14
20
|
]
|
qtype/base/logging.py
CHANGED
|
@@ -5,23 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def get_logger(name: str) -> logging.Logger:
|
|
9
|
-
"""Get a logger with the given name and consistent formatting."""
|
|
10
|
-
logger = logging.getLogger(f"qtype.{name}")
|
|
11
|
-
|
|
12
|
-
# Only configure if not already configured
|
|
13
|
-
if not logger.handlers:
|
|
14
|
-
handler = logging.StreamHandler()
|
|
15
|
-
formatter = logging.Formatter(
|
|
16
|
-
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
17
|
-
)
|
|
18
|
-
handler.setFormatter(formatter)
|
|
19
|
-
logger.addHandler(handler)
|
|
20
|
-
logger.setLevel(logging.INFO)
|
|
21
|
-
|
|
22
|
-
return logger
|
|
23
|
-
|
|
24
|
-
|
|
25
8
|
def configure_logging(
|
|
26
9
|
level: str = "INFO", format_string: str | None = None
|
|
27
10
|
) -> None:
|
qtype/base/resources.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Resource directory access utilities for QType package resources."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
from importlib.resources import files
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
# Regex for pymdownx snippets: --8<-- "path/to/file"
|
|
11
|
+
SNIPPET_REGEX = re.compile(r'--8<--\s+"([^"]+)"')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ResourceDirectory:
|
|
15
|
+
"""Abstraction for accessing resource directories (docs, examples, etc.)."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self, name: str, file_extension: str, resolve_snippets: bool = False
|
|
19
|
+
):
|
|
20
|
+
"""Initialize a resource directory.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
name: Directory name (e.g., "docs", "examples")
|
|
24
|
+
file_extension: File extension to search for (e.g., ".md", ".yaml")
|
|
25
|
+
resolve_snippets: Whether to resolve MkDocs snippets in file content
|
|
26
|
+
"""
|
|
27
|
+
self.name = name
|
|
28
|
+
self.file_extension = file_extension
|
|
29
|
+
self.resolve_snippets = resolve_snippets
|
|
30
|
+
self._path_cache: Path | None = None
|
|
31
|
+
|
|
32
|
+
def get_path(self) -> Path:
|
|
33
|
+
"""Get the path to this resource directory.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Path to the resource directory, trying installed package first,
|
|
37
|
+
then falling back to development path.
|
|
38
|
+
"""
|
|
39
|
+
if self._path_cache is not None:
|
|
40
|
+
return self._path_cache
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
# Try to get from installed package
|
|
44
|
+
resource_root = files("qtype") / self.name
|
|
45
|
+
# Check if it exists by trying to iterate
|
|
46
|
+
list(resource_root.iterdir())
|
|
47
|
+
self._path_cache = Path(str(resource_root))
|
|
48
|
+
except (FileNotFoundError, AttributeError, TypeError):
|
|
49
|
+
# Fall back to development path
|
|
50
|
+
self._path_cache = Path(__file__).parent.parent.parent / self.name
|
|
51
|
+
|
|
52
|
+
return self._path_cache
|
|
53
|
+
|
|
54
|
+
def get_file(self, file_path: str) -> str:
|
|
55
|
+
"""Get the content of a specific file.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
file_path: Relative path to the file from the resource root.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
The full content of the file.
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
FileNotFoundError: If the specified file doesn't exist.
|
|
65
|
+
ValueError: If the path tries to access files outside the directory.
|
|
66
|
+
"""
|
|
67
|
+
resource_path = self.get_path()
|
|
68
|
+
|
|
69
|
+
# Resolve the requested file path
|
|
70
|
+
requested_file = (resource_path / file_path).resolve()
|
|
71
|
+
|
|
72
|
+
# Security check: ensure the resolved path is within resource directory
|
|
73
|
+
try:
|
|
74
|
+
requested_file.relative_to(resource_path.resolve())
|
|
75
|
+
except ValueError as e:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
f"Invalid path: '{file_path}' is outside {self.name} directory"
|
|
78
|
+
) from e
|
|
79
|
+
|
|
80
|
+
if not requested_file.exists():
|
|
81
|
+
raise FileNotFoundError(
|
|
82
|
+
(
|
|
83
|
+
f"{self.name.capitalize()} file not found: '{file_path}'. "
|
|
84
|
+
f"Use list_{self.name} to see available files."
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not requested_file.is_file():
|
|
89
|
+
raise ValueError(f"Path is not a file: '{file_path}'")
|
|
90
|
+
|
|
91
|
+
content = requested_file.read_text(encoding="utf-8")
|
|
92
|
+
|
|
93
|
+
# Apply snippet resolution if enabled
|
|
94
|
+
if self.resolve_snippets:
|
|
95
|
+
content = _resolve_snippets(content, requested_file, self)
|
|
96
|
+
|
|
97
|
+
return content
|
|
98
|
+
|
|
99
|
+
def list_files(self) -> list[str]:
|
|
100
|
+
"""List all files in this resource directory.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Sorted list of relative paths to all files with the configured extension.
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
FileNotFoundError: If the resource directory doesn't exist.
|
|
107
|
+
"""
|
|
108
|
+
resource_path = self.get_path()
|
|
109
|
+
|
|
110
|
+
if not resource_path.exists():
|
|
111
|
+
raise FileNotFoundError(
|
|
112
|
+
(
|
|
113
|
+
f"{self.name.capitalize()} directory not found: "
|
|
114
|
+
f"{resource_path}"
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Find all files with the configured extension
|
|
119
|
+
pattern = f"*{self.file_extension}"
|
|
120
|
+
files_list = []
|
|
121
|
+
for file in resource_path.rglob(pattern):
|
|
122
|
+
# Get relative path from resource root
|
|
123
|
+
rel_path = file.relative_to(resource_path)
|
|
124
|
+
files_list.append(str(rel_path))
|
|
125
|
+
|
|
126
|
+
return sorted(files_list)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _resolve_snippets(
|
|
130
|
+
content: str, base_path: Path, docs_resource: ResourceDirectory
|
|
131
|
+
) -> str:
|
|
132
|
+
"""Recursively resolve MkDocs snippets in markdown content.
|
|
133
|
+
|
|
134
|
+
Mimics the behavior of pymdownx.snippets.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
content: The markdown content to process
|
|
138
|
+
base_path: Path to the file being processed (for resolving relative paths)
|
|
139
|
+
docs_resource: The docs ResourceDirectory for resolving snippet paths
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Content with all snippets resolved
|
|
143
|
+
"""
|
|
144
|
+
docs_root = docs_resource.get_path()
|
|
145
|
+
project_root = docs_root.parent
|
|
146
|
+
|
|
147
|
+
def replace_match(match: re.Match) -> str:
|
|
148
|
+
snippet_path = match.group(1)
|
|
149
|
+
|
|
150
|
+
# pymdownx logic: try relative to current file, then docs, then project
|
|
151
|
+
candidates = [
|
|
152
|
+
base_path.parent / snippet_path, # Relative to the doc file
|
|
153
|
+
docs_root / snippet_path, # Relative to docs root
|
|
154
|
+
project_root / snippet_path, # Relative to project root
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
for candidate in candidates:
|
|
158
|
+
if candidate.exists() and candidate.is_file():
|
|
159
|
+
# Recursively resolve snippets inside the included file
|
|
160
|
+
return _resolve_snippets(
|
|
161
|
+
candidate.read_text(encoding="utf-8"),
|
|
162
|
+
candidate,
|
|
163
|
+
docs_resource,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return f"> [!WARNING] Could not resolve snippet: {snippet_path}"
|
|
167
|
+
|
|
168
|
+
return SNIPPET_REGEX.sub(replace_match, content)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# Initialize singleton resource directories
|
|
172
|
+
_docs_resource = ResourceDirectory("docs", ".md", resolve_snippets=True)
|
|
173
|
+
_examples_resource = ResourceDirectory("examples", ".yaml")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@lru_cache(maxsize=1)
|
|
177
|
+
def get_docs_resource() -> ResourceDirectory:
|
|
178
|
+
"""Get the singleton docs resource directory.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
ResourceDirectory instance for documentation files.
|
|
182
|
+
"""
|
|
183
|
+
return _docs_resource
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@lru_cache(maxsize=1)
|
|
187
|
+
def get_examples_resource() -> ResourceDirectory:
|
|
188
|
+
"""Get the singleton examples resource directory.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
ResourceDirectory instance for example files.
|
|
192
|
+
"""
|
|
193
|
+
return _examples_resource
|
qtype/cli.py
CHANGED
|
@@ -7,9 +7,9 @@ import importlib
|
|
|
7
7
|
import logging
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
-
from qtype.base.logging import
|
|
10
|
+
from qtype.base.logging import configure_logging
|
|
11
11
|
|
|
12
|
-
logger =
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
14
|
try:
|
|
15
15
|
from importlib.metadata import entry_points
|
|
@@ -59,9 +59,8 @@ def _discover_local_commands(subparsers: argparse._SubParsersAction) -> None:
|
|
|
59
59
|
f"Built-in command module {module_name} does not have a 'parser' function"
|
|
60
60
|
)
|
|
61
61
|
except Exception as e:
|
|
62
|
-
logging.
|
|
63
|
-
f"Failed to load built-in command module {module_name}: {e}"
|
|
64
|
-
exc_info=True,
|
|
62
|
+
logging.debug(
|
|
63
|
+
f"Failed to load built-in command module {module_name}: {e} -- you may need the mcp or interpreter extras."
|
|
65
64
|
)
|
|
66
65
|
|
|
67
66
|
|
|
@@ -133,10 +132,7 @@ def main() -> None:
|
|
|
133
132
|
args = parser.parse_args()
|
|
134
133
|
|
|
135
134
|
# Set logging level based on user input
|
|
136
|
-
|
|
137
|
-
level=getattr(logging, args.log_level),
|
|
138
|
-
format="%(asctime)s - %(levelname)s: %(message)s",
|
|
139
|
-
)
|
|
135
|
+
configure_logging(level=args.log_level)
|
|
140
136
|
|
|
141
137
|
# Dispatch to the selected subcommand
|
|
142
138
|
args.func(args)
|