qtype 0.0.16__py3-none-any.whl → 0.1.1__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 (128) hide show
  1. qtype/application/commons/tools.py +1 -1
  2. qtype/application/converters/tools_from_api.py +5 -5
  3. qtype/application/converters/tools_from_module.py +2 -2
  4. qtype/application/converters/types.py +14 -43
  5. qtype/application/documentation.py +1 -1
  6. qtype/application/facade.py +94 -73
  7. qtype/base/types.py +227 -7
  8. qtype/cli.py +4 -0
  9. qtype/commands/convert.py +20 -8
  10. qtype/commands/generate.py +19 -27
  11. qtype/commands/run.py +73 -36
  12. qtype/commands/serve.py +74 -54
  13. qtype/commands/validate.py +34 -8
  14. qtype/commands/visualize.py +46 -22
  15. qtype/dsl/__init__.py +6 -5
  16. qtype/dsl/custom_types.py +1 -1
  17. qtype/dsl/domain_types.py +65 -5
  18. qtype/dsl/linker.py +384 -0
  19. qtype/dsl/loader.py +315 -0
  20. qtype/dsl/model.py +612 -363
  21. qtype/dsl/parser.py +200 -0
  22. qtype/dsl/types.py +50 -0
  23. qtype/interpreter/api.py +57 -136
  24. qtype/interpreter/auth/aws.py +19 -9
  25. qtype/interpreter/auth/generic.py +93 -16
  26. qtype/interpreter/base/base_step_executor.py +436 -0
  27. qtype/interpreter/base/batch_step_executor.py +171 -0
  28. qtype/interpreter/base/exceptions.py +50 -0
  29. qtype/interpreter/base/executor_context.py +74 -0
  30. qtype/interpreter/base/factory.py +117 -0
  31. qtype/interpreter/base/progress_tracker.py +110 -0
  32. qtype/interpreter/base/secrets.py +339 -0
  33. qtype/interpreter/base/step_cache.py +74 -0
  34. qtype/interpreter/base/stream_emitter.py +469 -0
  35. qtype/interpreter/conversions.py +462 -22
  36. qtype/interpreter/converters.py +77 -0
  37. qtype/interpreter/endpoints.py +355 -0
  38. qtype/interpreter/executors/agent_executor.py +242 -0
  39. qtype/interpreter/executors/aggregate_executor.py +93 -0
  40. qtype/interpreter/executors/decoder_executor.py +163 -0
  41. qtype/interpreter/executors/doc_to_text_executor.py +112 -0
  42. qtype/interpreter/executors/document_embedder_executor.py +107 -0
  43. qtype/interpreter/executors/document_search_executor.py +122 -0
  44. qtype/interpreter/executors/document_source_executor.py +118 -0
  45. qtype/interpreter/executors/document_splitter_executor.py +105 -0
  46. qtype/interpreter/executors/echo_executor.py +63 -0
  47. qtype/interpreter/executors/field_extractor_executor.py +160 -0
  48. qtype/interpreter/executors/file_source_executor.py +101 -0
  49. qtype/interpreter/executors/file_writer_executor.py +110 -0
  50. qtype/interpreter/executors/index_upsert_executor.py +228 -0
  51. qtype/interpreter/executors/invoke_embedding_executor.py +92 -0
  52. qtype/interpreter/executors/invoke_flow_executor.py +51 -0
  53. qtype/interpreter/executors/invoke_tool_executor.py +358 -0
  54. qtype/interpreter/executors/llm_inference_executor.py +272 -0
  55. qtype/interpreter/executors/prompt_template_executor.py +78 -0
  56. qtype/interpreter/executors/sql_source_executor.py +106 -0
  57. qtype/interpreter/executors/vector_search_executor.py +91 -0
  58. qtype/interpreter/flow.py +159 -22
  59. qtype/interpreter/metadata_api.py +115 -0
  60. qtype/interpreter/resource_cache.py +5 -4
  61. qtype/interpreter/rich_progress.py +225 -0
  62. qtype/interpreter/stream/chat/__init__.py +15 -0
  63. qtype/interpreter/stream/chat/converter.py +391 -0
  64. qtype/interpreter/{chat → stream/chat}/file_conversions.py +2 -2
  65. qtype/interpreter/stream/chat/ui_request_to_domain_type.py +140 -0
  66. qtype/interpreter/stream/chat/vercel.py +609 -0
  67. qtype/interpreter/stream/utils/__init__.py +15 -0
  68. qtype/interpreter/stream/utils/build_vercel_ai_formatter.py +74 -0
  69. qtype/interpreter/stream/utils/callback_to_stream.py +66 -0
  70. qtype/interpreter/stream/utils/create_streaming_response.py +18 -0
  71. qtype/interpreter/stream/utils/default_chat_extract_text.py +20 -0
  72. qtype/interpreter/stream/utils/error_streaming_response.py +20 -0
  73. qtype/interpreter/telemetry.py +135 -8
  74. qtype/interpreter/tools/__init__.py +5 -0
  75. qtype/interpreter/tools/function_tool_helper.py +265 -0
  76. qtype/interpreter/types.py +330 -0
  77. qtype/interpreter/typing.py +83 -89
  78. qtype/interpreter/ui/404/index.html +1 -1
  79. qtype/interpreter/ui/404.html +1 -1
  80. qtype/interpreter/ui/_next/static/{nUaw6_IwRwPqkzwe5s725 → 20HoJN6otZ_LyHLHpCPE6}/_buildManifest.js +1 -1
  81. qtype/interpreter/ui/_next/static/chunks/{393-8fd474427f8e19ce.js → 434-b2112d19f25c44ff.js} +3 -3
  82. qtype/interpreter/ui/_next/static/chunks/app/page-8c67d16ac90d23cb.js +1 -0
  83. qtype/interpreter/ui/_next/static/chunks/ba12c10f-546f2714ff8abc66.js +1 -0
  84. qtype/interpreter/ui/_next/static/css/8a8d1269e362fef7.css +3 -0
  85. qtype/interpreter/ui/icon.png +0 -0
  86. qtype/interpreter/ui/index.html +1 -1
  87. qtype/interpreter/ui/index.txt +4 -4
  88. qtype/semantic/checker.py +583 -0
  89. qtype/semantic/generate.py +262 -83
  90. qtype/semantic/loader.py +95 -0
  91. qtype/semantic/model.py +436 -159
  92. qtype/semantic/resolver.py +63 -19
  93. qtype/semantic/visualize.py +28 -31
  94. {qtype-0.0.16.dist-info → qtype-0.1.1.dist-info}/METADATA +16 -3
  95. qtype-0.1.1.dist-info/RECORD +135 -0
  96. qtype/dsl/base_types.py +0 -38
  97. qtype/dsl/validator.py +0 -465
  98. qtype/interpreter/batch/__init__.py +0 -0
  99. qtype/interpreter/batch/file_sink_source.py +0 -162
  100. qtype/interpreter/batch/flow.py +0 -95
  101. qtype/interpreter/batch/sql_source.py +0 -92
  102. qtype/interpreter/batch/step.py +0 -74
  103. qtype/interpreter/batch/types.py +0 -41
  104. qtype/interpreter/batch/utils.py +0 -178
  105. qtype/interpreter/chat/chat_api.py +0 -237
  106. qtype/interpreter/chat/vercel.py +0 -314
  107. qtype/interpreter/exceptions.py +0 -10
  108. qtype/interpreter/step.py +0 -67
  109. qtype/interpreter/steps/__init__.py +0 -0
  110. qtype/interpreter/steps/agent.py +0 -114
  111. qtype/interpreter/steps/condition.py +0 -36
  112. qtype/interpreter/steps/decoder.py +0 -88
  113. qtype/interpreter/steps/llm_inference.py +0 -171
  114. qtype/interpreter/steps/prompt_template.py +0 -54
  115. qtype/interpreter/steps/search.py +0 -24
  116. qtype/interpreter/steps/tool.py +0 -219
  117. qtype/interpreter/streaming_helpers.py +0 -123
  118. qtype/interpreter/ui/_next/static/chunks/app/page-7e26b6156cfb55d3.js +0 -1
  119. qtype/interpreter/ui/_next/static/chunks/ba12c10f-22556063851a6df2.js +0 -1
  120. qtype/interpreter/ui/_next/static/css/b40532b0db09cce3.css +0 -3
  121. qtype/interpreter/ui/favicon.ico +0 -0
  122. qtype/loader.py +0 -390
  123. qtype-0.0.16.dist-info/RECORD +0 -106
  124. /qtype/interpreter/ui/_next/static/{nUaw6_IwRwPqkzwe5s725 → 20HoJN6otZ_LyHLHpCPE6}/_ssgManifest.js +0 -0
  125. {qtype-0.0.16.dist-info → qtype-0.1.1.dist-info}/WHEEL +0 -0
  126. {qtype-0.0.16.dist-info → qtype-0.1.1.dist-info}/entry_points.txt +0 -0
  127. {qtype-0.0.16.dist-info → qtype-0.1.1.dist-info}/licenses/LICENSE +0 -0
  128. {qtype-0.0.16.dist-info → qtype-0.1.1.dist-info}/top_level.txt +0 -0
qtype/dsl/loader.py ADDED
@@ -0,0 +1,315 @@
1
+ """
2
+ YAML loading with environment variable and file inclusion support.
3
+
4
+ This module provides two explicit functions for loading YAML:
5
+ - load_yaml_file(path): Load YAML from a file path or URI
6
+ - load_yaml_string(content, base_path): Load YAML from a string
7
+
8
+ Both support:
9
+ - Environment variable substitution (${VAR} or ${VAR:default})
10
+ - File inclusion (!include and !include_raw)
11
+ - Multiple URI schemes via fsspec (local, http, s3, etc.)
12
+
13
+ Example:
14
+ # Load from file
15
+ data = load_yaml_file("config.yaml")
16
+ data = load_yaml_file("s3://bucket/config.yaml")
17
+
18
+ # Load from string
19
+ yaml_content = "name: test\\nvalue: ${ENV_VAR}"
20
+ data = load_yaml_string(yaml_content)
21
+
22
+ # Load string with includes (requires base_path)
23
+ data = load_yaml_string(yaml_content, base_path="/path/to/configs")
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import os
29
+ import re
30
+ from pathlib import Path
31
+ from typing import Any
32
+
33
+ import fsspec
34
+ import yaml
35
+ from dotenv import load_dotenv
36
+
37
+
38
+ class YAMLLoadError(Exception):
39
+ """Error during YAML loading or parsing."""
40
+
41
+ def __init__(
42
+ self,
43
+ message: str,
44
+ line: int | None = None,
45
+ column: int | None = None,
46
+ source: str | None = None,
47
+ original_error: Exception | None = None,
48
+ ) -> None:
49
+ self.message = message
50
+ self.line = line
51
+ self.column = column
52
+ self.source = source
53
+ self.original_error = original_error
54
+ super().__init__(self._format_message())
55
+
56
+ def _format_message(self) -> str:
57
+ """Format error message with location information."""
58
+ parts = []
59
+ if self.source:
60
+ parts.append(f"in {self.source}")
61
+ if self.line is not None:
62
+ location = f"line {self.line + 1}"
63
+ if self.column is not None:
64
+ location += f", column {self.column + 1}"
65
+ parts.append(location)
66
+
67
+ if parts:
68
+ return f"{self.message} ({', '.join(parts)})"
69
+ return self.message
70
+
71
+
72
+ class YAMLLoader(yaml.SafeLoader):
73
+ """YAML loader with env var substitution and file inclusion."""
74
+
75
+ def __init__(self, stream: Any, base_path: str | None = None) -> None:
76
+ super().__init__(stream)
77
+ self.base_path = base_path or str(Path.cwd())
78
+
79
+
80
+ def _substitute_env_vars(value: str) -> str:
81
+ """
82
+ Substitute environment variables in a string.
83
+
84
+ Supports ${VAR_NAME} or ${VAR_NAME:default} syntax.
85
+
86
+ Args:
87
+ value: String containing environment variable references
88
+
89
+ Returns:
90
+ String with environment variables substituted
91
+
92
+ Raises:
93
+ ValueError: If required environment variable is not found
94
+ """
95
+ pattern = r"\$\{([^}:]+)(?::([^}]*))?\}"
96
+
97
+ def replace_env_var(match: re.Match[str]) -> str:
98
+ var_name = match.group(1)
99
+ default_value = match.group(2)
100
+
101
+ env_value = os.getenv(var_name)
102
+
103
+ if env_value is not None:
104
+ return env_value
105
+ elif default_value is not None:
106
+ return default_value
107
+ else:
108
+ raise ValueError(
109
+ f"Environment variable '{var_name}' is required but not set"
110
+ )
111
+
112
+ return re.sub(pattern, replace_env_var, value)
113
+
114
+
115
+ def _resolve_path(base_path: str, target_path: str) -> str:
116
+ """
117
+ Resolve a target path relative to base path.
118
+
119
+ Uses fsspec's URL joining logic which handles both local paths and URIs.
120
+
121
+ Args:
122
+ base_path: Base path or URI
123
+ target_path: Target path to resolve (relative or absolute)
124
+
125
+ Returns:
126
+ Resolved absolute path or URI
127
+ """
128
+ # If target is already absolute (has scheme or starts with /), use as-is
129
+ from urllib.parse import urljoin, urlparse
130
+
131
+ parsed = urlparse(target_path)
132
+ if parsed.scheme or target_path.startswith("/"):
133
+ return target_path
134
+
135
+ # Check if base is URL-like
136
+ base_parsed = urlparse(base_path)
137
+ if base_parsed.scheme:
138
+ # URL-based resolution
139
+ return urljoin(base_path, target_path)
140
+ else:
141
+ # Local file resolution
142
+ base_path_obj = Path(base_path)
143
+ if not base_path_obj.is_dir():
144
+ base_path_obj = base_path_obj.parent
145
+ return str(base_path_obj / target_path)
146
+
147
+
148
+ def _env_var_constructor(loader: YAMLLoader, node: yaml.ScalarNode) -> str:
149
+ """Constructor for environment variable substitution."""
150
+ value = loader.construct_scalar(node)
151
+ return _substitute_env_vars(value)
152
+
153
+
154
+ def _include_constructor(loader: YAMLLoader, node: yaml.ScalarNode) -> Any:
155
+ """Constructor for !include tag to load external YAML files."""
156
+ file_path = loader.construct_scalar(node)
157
+ resolved_path = _resolve_path(loader.base_path, file_path)
158
+
159
+ try:
160
+ with fsspec.open(resolved_path, "r", encoding="utf-8") as f:
161
+ content = f.read() # type: ignore[misc]
162
+ # Create a partial function to pass base_path to YAMLLoader
163
+ from functools import partial
164
+
165
+ loader_class = partial(YAMLLoader, base_path=resolved_path)
166
+ return yaml.load(content, loader_class) # type: ignore[arg-type]
167
+ except (FileNotFoundError, IOError, OSError) as e:
168
+ raise FileNotFoundError(
169
+ f"Failed to load included file '{resolved_path}': {e}"
170
+ ) from e
171
+
172
+
173
+ def _include_raw_constructor(loader: YAMLLoader, node: yaml.ScalarNode) -> str:
174
+ """Constructor for !include_raw tag to load external text files."""
175
+ file_path = loader.construct_scalar(node)
176
+ resolved_path = _resolve_path(loader.base_path, file_path)
177
+
178
+ try:
179
+ with fsspec.open(resolved_path, "r", encoding="utf-8") as f:
180
+ return f.read() # type: ignore[no-any-return]
181
+ except (FileNotFoundError, IOError, OSError) as e:
182
+ raise FileNotFoundError(
183
+ f"Failed to load included file '{resolved_path}': {e}"
184
+ ) from e
185
+
186
+
187
+ # Register constructors
188
+ YAMLLoader.add_constructor("tag:yaml.org,2002:str", _env_var_constructor)
189
+ YAMLLoader.add_constructor("!include", _include_constructor)
190
+ YAMLLoader.add_constructor("!include_raw", _include_raw_constructor)
191
+
192
+
193
+ def load_yaml_file(path: str | Path) -> dict[str, Any]:
194
+ """
195
+ Load YAML from a file path or URI.
196
+
197
+ Supports multiple URI schemes via fsspec (local files, http, s3, etc.).
198
+ Automatically loads .env files from the source directory.
199
+
200
+ Args:
201
+ path: File path or URI to load
202
+
203
+ Returns:
204
+ Parsed YAML as dictionary
205
+
206
+ Raises:
207
+ YAMLLoadError: If YAML parsing fails
208
+ FileNotFoundError: If file doesn't exist
209
+ ValueError: If required environment variable is missing
210
+ """
211
+ source_str = str(path)
212
+
213
+ # Load .env file if it exists in the source directory
214
+ try:
215
+ from urllib.parse import urlparse
216
+
217
+ parsed = urlparse(source_str)
218
+ if parsed.scheme in ["file", ""]:
219
+ # Local file - load .env from same directory
220
+ source_path = Path(parsed.path if parsed.path else source_str)
221
+ if source_path.is_file():
222
+ env_dir = source_path.parent
223
+ env_file = env_dir / ".env"
224
+ if env_file.exists():
225
+ load_dotenv(env_file)
226
+ except Exception:
227
+ pass
228
+
229
+ # Also try cwd
230
+ load_dotenv()
231
+
232
+ # Load file content
233
+ try:
234
+ with fsspec.open(source_str, "r", encoding="utf-8") as f:
235
+ content = f.read() # type: ignore[misc]
236
+ except FileNotFoundError as e:
237
+ raise FileNotFoundError(f"File not found: {source_str}") from e
238
+
239
+ return _parse_yaml(content, base_path=source_str, source_name=source_str)
240
+
241
+
242
+ def load_yaml_string(
243
+ content: str, base_path: str | Path | None = None
244
+ ) -> dict[str, Any]:
245
+ """
246
+ Load YAML from a string.
247
+
248
+ Args:
249
+ content: Raw YAML content as string
250
+ base_path: Base path for resolving relative includes (default: cwd)
251
+
252
+ Returns:
253
+ Parsed YAML as dictionary
254
+
255
+ Raises:
256
+ YAMLLoadError: If YAML parsing fails
257
+ ValueError: If required environment variable is missing
258
+ """
259
+ load_dotenv()
260
+
261
+ base = str(base_path) if base_path else str(Path.cwd())
262
+ return _parse_yaml(content, base_path=base, source_name="<string>")
263
+
264
+
265
+ def _parse_yaml(
266
+ content: str, base_path: str, source_name: str
267
+ ) -> dict[str, Any]:
268
+ """
269
+ Parse YAML content with environment variable substitution and includes.
270
+
271
+ Args:
272
+ content: YAML content to parse
273
+ base_path: Base path for resolving relative includes
274
+ source_name: Source name for error messages
275
+
276
+ Returns:
277
+ Parsed YAML as dictionary
278
+
279
+ Raises:
280
+ YAMLLoadError: If YAML parsing fails
281
+ """
282
+ try:
283
+ from functools import partial
284
+
285
+ loader_class = partial(YAMLLoader, base_path=base_path)
286
+ result = yaml.load(content, loader_class) # type: ignore[arg-type]
287
+ return result # type: ignore[no-any-return]
288
+ except yaml.YAMLError as e:
289
+ # Extract line/column information if available
290
+ line = None
291
+ column = None
292
+
293
+ if hasattr(e, "problem_mark") and e.problem_mark: # type: ignore[attr-defined]
294
+ line = e.problem_mark.line # type: ignore[attr-defined]
295
+ column = e.problem_mark.column # type: ignore[attr-defined]
296
+
297
+ # Format error message
298
+ error_msg = str(e)
299
+ if hasattr(e, "problem"):
300
+ error_msg = e.problem or error_msg # type: ignore[attr-defined]
301
+
302
+ raise YAMLLoadError(
303
+ message=f"YAML parsing error: {error_msg}",
304
+ line=line,
305
+ column=column,
306
+ source=source_name,
307
+ original_error=e,
308
+ ) from e
309
+ except ValueError as e:
310
+ # Environment variable errors
311
+ raise YAMLLoadError(
312
+ message=str(e),
313
+ source=source_name,
314
+ original_error=e,
315
+ ) from e