qtype 0.0.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.
- qtype/__init__.py +0 -0
- qtype/cli.py +73 -0
- qtype/commands/__init__.py +5 -0
- qtype/commands/convert.py +76 -0
- qtype/commands/generate.py +107 -0
- qtype/commands/run.py +200 -0
- qtype/commands/validate.py +83 -0
- qtype/commons/__init__.py +0 -0
- qtype/commons/generate.py +88 -0
- qtype/commons/tools.py +192 -0
- qtype/converters/__init__.py +0 -0
- qtype/converters/tools_from_api.py +24 -0
- qtype/converters/tools_from_module.py +326 -0
- qtype/converters/types.py +20 -0
- qtype/dsl/__init__.py +1 -0
- qtype/dsl/base_types.py +31 -0
- qtype/dsl/document.py +108 -0
- qtype/dsl/domain_types.py +56 -0
- qtype/dsl/model.py +685 -0
- qtype/dsl/validator.py +439 -0
- qtype/interpreter/__init__.py +1 -0
- qtype/interpreter/api.py +104 -0
- qtype/interpreter/conversions.py +148 -0
- qtype/interpreter/exceptions.py +10 -0
- qtype/interpreter/flow.py +37 -0
- qtype/interpreter/resource_cache.py +37 -0
- qtype/interpreter/step.py +67 -0
- qtype/interpreter/steps/__init__.py +0 -0
- qtype/interpreter/steps/agent.py +114 -0
- qtype/interpreter/steps/condition.py +36 -0
- qtype/interpreter/steps/decoder.py +84 -0
- qtype/interpreter/steps/llm_inference.py +127 -0
- qtype/interpreter/steps/prompt_template.py +54 -0
- qtype/interpreter/steps/search.py +24 -0
- qtype/interpreter/steps/tool.py +53 -0
- qtype/interpreter/telemetry.py +16 -0
- qtype/interpreter/typing.py +78 -0
- qtype/loader.py +341 -0
- qtype/semantic/__init__.py +0 -0
- qtype/semantic/errors.py +4 -0
- qtype/semantic/generate.py +383 -0
- qtype/semantic/model.py +354 -0
- qtype/semantic/resolver.py +97 -0
- qtype-0.0.1.dist-info/METADATA +120 -0
- qtype-0.0.1.dist-info/RECORD +49 -0
- qtype-0.0.1.dist-info/WHEEL +5 -0
- qtype-0.0.1.dist-info/entry_points.txt +2 -0
- qtype-0.0.1.dist-info/licenses/LICENSE +202 -0
- qtype-0.0.1.dist-info/top_level.txt +1 -0
qtype/dsl/model.py
ADDED
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from abc import ABC
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Type, Union
|
|
7
|
+
|
|
8
|
+
from pydantic import Field, RootModel, model_validator
|
|
9
|
+
|
|
10
|
+
import qtype.dsl.domain_types as domain_types
|
|
11
|
+
from qtype.dsl.base_types import PrimitiveTypeEnum, StrictBaseModel
|
|
12
|
+
from qtype.dsl.domain_types import ChatContent, ChatMessage, Embedding
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StructuralTypeEnum(str, Enum):
|
|
16
|
+
"""Represents a structured type that can be used in the DSL."""
|
|
17
|
+
|
|
18
|
+
object = "object"
|
|
19
|
+
array = "array"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
DOMAIN_CLASSES = {
|
|
23
|
+
name: obj
|
|
24
|
+
for name, obj in inspect.getmembers(domain_types)
|
|
25
|
+
if inspect.isclass(obj) and obj.__module__ == domain_types.__name__
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _resolve_variable_type(parsed_type: Any) -> Any:
|
|
30
|
+
"""Resolve a type string to its corresponding PrimitiveTypeEnum or return as is."""
|
|
31
|
+
# If the type is already resolved or is a structured definition, pass it through.
|
|
32
|
+
if not isinstance(parsed_type, str):
|
|
33
|
+
return parsed_type
|
|
34
|
+
|
|
35
|
+
# --- Case 1: The type is a string ---
|
|
36
|
+
# Try to resolve it as a primitive type first.
|
|
37
|
+
try:
|
|
38
|
+
return PrimitiveTypeEnum(parsed_type)
|
|
39
|
+
except ValueError:
|
|
40
|
+
pass # Not a primitive, continue to the next check.
|
|
41
|
+
|
|
42
|
+
# Try to resolve it as a built-in Domain Entity class.
|
|
43
|
+
# (Assuming domain_types and inspect are defined elsewhere)
|
|
44
|
+
if parsed_type in DOMAIN_CLASSES:
|
|
45
|
+
return DOMAIN_CLASSES[parsed_type]
|
|
46
|
+
|
|
47
|
+
# If it's not a primitive or a known domain entity, return it as a string.
|
|
48
|
+
# This assumes it might be a reference ID to another custom type.
|
|
49
|
+
return parsed_type
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Variable(StrictBaseModel):
|
|
53
|
+
"""Schema for a variable that can serve as input, output, or parameter within the DSL."""
|
|
54
|
+
|
|
55
|
+
id: str = Field(
|
|
56
|
+
...,
|
|
57
|
+
description="Unique ID of the variable. Referenced in prompts or steps.",
|
|
58
|
+
)
|
|
59
|
+
type: VariableType | str = Field(
|
|
60
|
+
...,
|
|
61
|
+
description=("Type of data expected or produced."),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@model_validator(mode="before")
|
|
65
|
+
@classmethod
|
|
66
|
+
def resolve_type(cls, data: Any) -> Any:
|
|
67
|
+
if isinstance(data, dict) and "type" in data:
|
|
68
|
+
data["type"] = _resolve_variable_type(data["type"]) # type: ignore
|
|
69
|
+
return data
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TypeDefinitionBase(StrictBaseModel, ABC):
|
|
73
|
+
id: str = Field(description="The unique identifier for this custom type.")
|
|
74
|
+
kind: StructuralTypeEnum = Field(
|
|
75
|
+
...,
|
|
76
|
+
description="The kind of structure this type represents (object/array).",
|
|
77
|
+
)
|
|
78
|
+
description: str | None = Field(
|
|
79
|
+
None, description="A description of what this type represents."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ObjectTypeDefinition(TypeDefinitionBase):
|
|
84
|
+
kind: StructuralTypeEnum = StructuralTypeEnum.object
|
|
85
|
+
properties: dict[str, VariableType | str] | None = Field(
|
|
86
|
+
None, description="Defines the nested properties."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@model_validator(mode="after")
|
|
90
|
+
def resolve_type(self) -> "ObjectTypeDefinition":
|
|
91
|
+
"""Resolve the type string to its corresponding PrimitiveTypeEnum."""
|
|
92
|
+
# Pydantic doesn't properly handle enums as strings in model validation,
|
|
93
|
+
if self.properties:
|
|
94
|
+
for key, value in self.properties.items():
|
|
95
|
+
if isinstance(value, str):
|
|
96
|
+
self.properties[key] = _resolve_variable_type(value)
|
|
97
|
+
return self
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ArrayTypeDefinition(TypeDefinitionBase):
|
|
101
|
+
kind: StructuralTypeEnum = StructuralTypeEnum.array
|
|
102
|
+
type: VariableType | str = Field(
|
|
103
|
+
..., description="The type of items in the array."
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
@model_validator(mode="before")
|
|
107
|
+
@classmethod
|
|
108
|
+
def resolve_type(cls, data: Any) -> Any:
|
|
109
|
+
if isinstance(data, dict) and "type" in data:
|
|
110
|
+
# If the type is a string, resolve it to PrimitiveTypeEnum or Domain Entity class.
|
|
111
|
+
data["type"] = _resolve_variable_type(data["type"]) # type: ignore
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
TypeDefinition = ObjectTypeDefinition | ArrayTypeDefinition
|
|
116
|
+
VariableType = (
|
|
117
|
+
PrimitiveTypeEnum
|
|
118
|
+
| TypeDefinition
|
|
119
|
+
| Type[Embedding]
|
|
120
|
+
| Type[ChatMessage]
|
|
121
|
+
| Type[ChatContent]
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class Model(StrictBaseModel):
|
|
126
|
+
"""Describes a generative model configuration, including provider and model ID."""
|
|
127
|
+
|
|
128
|
+
id: str = Field(..., description="Unique ID for the model.")
|
|
129
|
+
auth: AuthorizationProvider | str | None = Field(
|
|
130
|
+
default=None,
|
|
131
|
+
description="AuthorizationProvider used for model access.",
|
|
132
|
+
)
|
|
133
|
+
inference_params: dict[str, Any] | None = Field(
|
|
134
|
+
default=None,
|
|
135
|
+
description="Optional inference parameters like temperature or max_tokens.",
|
|
136
|
+
)
|
|
137
|
+
model_id: str | None = Field(
|
|
138
|
+
default=None,
|
|
139
|
+
description="The specific model name or ID for the provider. If None, id is used",
|
|
140
|
+
)
|
|
141
|
+
# TODO(maybe): Make this an enum?
|
|
142
|
+
provider: str = Field(
|
|
143
|
+
..., description="Name of the provider, e.g., openai or anthropic."
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class EmbeddingModel(Model):
|
|
148
|
+
"""Describes an embedding model configuration, extending the base Model class."""
|
|
149
|
+
|
|
150
|
+
dimensions: int = Field(
|
|
151
|
+
...,
|
|
152
|
+
description="Dimensionality of the embedding vectors produced by this model.",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class Memory(StrictBaseModel):
|
|
157
|
+
"""Session or persistent memory used to store relevant conversation or state data across steps or turns."""
|
|
158
|
+
|
|
159
|
+
id: str = Field(..., description="Unique ID of the memory block.")
|
|
160
|
+
|
|
161
|
+
token_limit: int = Field(
|
|
162
|
+
default=100000,
|
|
163
|
+
description="Maximum number of tokens to store in memory.",
|
|
164
|
+
)
|
|
165
|
+
chat_history_token_ratio: float = Field(
|
|
166
|
+
default=0.7,
|
|
167
|
+
description="Ratio of chat history tokens to total memory tokens.",
|
|
168
|
+
)
|
|
169
|
+
token_flush_size: int = Field(
|
|
170
|
+
default=3000,
|
|
171
|
+
description="Size of memory to flush when it exceeds the token limit.",
|
|
172
|
+
)
|
|
173
|
+
# TODO: Add support for vectorstores and sql chat stores
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
#
|
|
177
|
+
# ---------------- Core Steps and Flow Components ----------------
|
|
178
|
+
#
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class Step(StrictBaseModel, ABC):
|
|
182
|
+
"""Base class for components that take inputs and produce outputs."""
|
|
183
|
+
|
|
184
|
+
id: str = Field(..., description="Unique ID of this component.")
|
|
185
|
+
inputs: list[Variable | str] | None = Field(
|
|
186
|
+
default=None,
|
|
187
|
+
description="Input variables required by this step.",
|
|
188
|
+
)
|
|
189
|
+
outputs: list[Variable | str] | None = Field(
|
|
190
|
+
default=None, description="Variable where output is stored."
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class PromptTemplate(Step):
|
|
195
|
+
"""Defines a prompt template with a string format and variable bindings.
|
|
196
|
+
This is used to generate prompts dynamically based on input variables."""
|
|
197
|
+
|
|
198
|
+
template: str = Field(
|
|
199
|
+
...,
|
|
200
|
+
description="String template for the prompt with variable placeholders.",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
@model_validator(mode="after")
|
|
204
|
+
def set_default_outputs(self) -> "PromptTemplate":
|
|
205
|
+
"""Set default output variable if none provided."""
|
|
206
|
+
if self.outputs is None:
|
|
207
|
+
self.outputs = [
|
|
208
|
+
Variable(id=f"{self.id}.prompt", type=PrimitiveTypeEnum.text)
|
|
209
|
+
]
|
|
210
|
+
if len(self.outputs) != 1:
|
|
211
|
+
raise ValueError(
|
|
212
|
+
"PromptTemplate steps must have exactly one output variable -- the result of applying the template."
|
|
213
|
+
)
|
|
214
|
+
return self
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class Condition(Step):
|
|
218
|
+
"""Conditional logic gate within a flow. Supports branching logic for execution based on variable values."""
|
|
219
|
+
|
|
220
|
+
# TODO: Add support for more complex conditions
|
|
221
|
+
else_: StepType | str | None = Field(
|
|
222
|
+
default=None,
|
|
223
|
+
alias="else",
|
|
224
|
+
description="Optional step to run if condition fails.",
|
|
225
|
+
)
|
|
226
|
+
equals: Variable | str | None = Field(
|
|
227
|
+
default=None, description="Match condition for equality check."
|
|
228
|
+
)
|
|
229
|
+
then: StepType | str = Field(
|
|
230
|
+
..., description="Step to run if condition matches."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
@model_validator(mode="after")
|
|
234
|
+
def set_default_outputs(self) -> "Condition":
|
|
235
|
+
"""Set default output variable if none provided."""
|
|
236
|
+
if not self.inputs or len(self.inputs) != 1:
|
|
237
|
+
raise ValueError(
|
|
238
|
+
"Condition steps must have exactly one input variable."
|
|
239
|
+
)
|
|
240
|
+
return self
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class Tool(Step, ABC):
|
|
244
|
+
"""
|
|
245
|
+
Base class for callable functions or external operations available to the model or as a step in a flow.
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
name: str = Field(..., description="Name of the tool function.")
|
|
249
|
+
description: str = Field(
|
|
250
|
+
..., description="Description of what the tool does."
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class PythonFunctionTool(Tool):
|
|
255
|
+
"""Tool that calls a Python function."""
|
|
256
|
+
|
|
257
|
+
function_name: str = Field(
|
|
258
|
+
..., description="Name of the Python function to call."
|
|
259
|
+
)
|
|
260
|
+
module_path: str = Field(
|
|
261
|
+
...,
|
|
262
|
+
description="Optional module path where the function is defined.",
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class APITool(Tool):
|
|
267
|
+
"""Tool that invokes an API endpoint."""
|
|
268
|
+
|
|
269
|
+
endpoint: str = Field(..., description="API endpoint URL to call.")
|
|
270
|
+
method: str = Field(
|
|
271
|
+
default="GET",
|
|
272
|
+
description="HTTP method to use (GET, POST, PUT, DELETE, etc.).",
|
|
273
|
+
)
|
|
274
|
+
auth: AuthorizationProvider | str | None = Field(
|
|
275
|
+
default=None,
|
|
276
|
+
description="Optional AuthorizationProvider for API authentication.",
|
|
277
|
+
)
|
|
278
|
+
headers: dict[str, str] | None = Field(
|
|
279
|
+
default=None,
|
|
280
|
+
description="Optional HTTP headers to include in the request.",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class LLMInference(Step):
|
|
285
|
+
"""Defines a step that performs inference using a language model.
|
|
286
|
+
It can take input variables and produce output variables based on the model's response."""
|
|
287
|
+
|
|
288
|
+
memory: Memory | str | None = Field(
|
|
289
|
+
default=None,
|
|
290
|
+
description="Memory object to retain context across interactions.",
|
|
291
|
+
)
|
|
292
|
+
model: ModelType | str = Field(
|
|
293
|
+
..., description="The model to use for inference."
|
|
294
|
+
)
|
|
295
|
+
system_message: str | None = Field(
|
|
296
|
+
default=None,
|
|
297
|
+
description="Optional system message to set the context for the model.",
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
@model_validator(mode="after")
|
|
301
|
+
def set_default_outputs(self) -> "LLMInference":
|
|
302
|
+
"""Set default output variable if none provided."""
|
|
303
|
+
if self.outputs is None:
|
|
304
|
+
self.outputs = [
|
|
305
|
+
Variable(id=f"{self.id}.response", type=PrimitiveTypeEnum.text)
|
|
306
|
+
]
|
|
307
|
+
return self
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class Agent(LLMInference):
|
|
311
|
+
"""Defines an agent that can perform tasks and make decisions based on user input and context."""
|
|
312
|
+
|
|
313
|
+
tools: list[ToolType | str] = Field(
|
|
314
|
+
..., description="List of tools available to the agent."
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class Flow(Step):
|
|
319
|
+
"""Defines a flow of steps that can be executed in sequence or parallel.
|
|
320
|
+
If input or output variables are not specified, they are inferred from
|
|
321
|
+
the first and last step, respectively.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
steps: list[StepType | str] = Field(
|
|
325
|
+
default_factory=list, description="List of steps or step IDs."
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class DecoderFormat(str, Enum):
|
|
330
|
+
"""Defines the format in which the decoder step processes data."""
|
|
331
|
+
|
|
332
|
+
json = "json"
|
|
333
|
+
xml = "xml"
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class Decoder(Step):
|
|
337
|
+
"""Defines a step that decodes string data into structured outputs.
|
|
338
|
+
|
|
339
|
+
If parsing fails, the step will raise an error and halt execution.
|
|
340
|
+
Use conditional logic in your flow to handle potential parsing errors.
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
format: DecoderFormat = Field(
|
|
344
|
+
DecoderFormat.json,
|
|
345
|
+
description="Format in which the decoder processes data. Defaults to JSON.",
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
@model_validator(mode="after")
|
|
349
|
+
def set_default_outputs(self) -> "Decoder":
|
|
350
|
+
"""Set default output variable if none provided."""
|
|
351
|
+
|
|
352
|
+
if (
|
|
353
|
+
self.inputs is None
|
|
354
|
+
or len(self.inputs) != 1
|
|
355
|
+
or (
|
|
356
|
+
isinstance(self.inputs[0], Variable)
|
|
357
|
+
and self.inputs[0].type != PrimitiveTypeEnum.text
|
|
358
|
+
)
|
|
359
|
+
):
|
|
360
|
+
raise ValueError(
|
|
361
|
+
f"Decoder steps must have exactly one input variable of type 'text'. Found: {self.inputs}"
|
|
362
|
+
)
|
|
363
|
+
if self.outputs is None:
|
|
364
|
+
raise ValueError(
|
|
365
|
+
"Decoder steps must have at least one output variable defined."
|
|
366
|
+
)
|
|
367
|
+
return self
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
#
|
|
371
|
+
# ---------------- Observability and Authentication Components ----------------
|
|
372
|
+
#
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class AuthorizationProvider(StrictBaseModel):
|
|
376
|
+
"""Defines how tools or providers authenticate with APIs, such as OAuth2 or API keys."""
|
|
377
|
+
|
|
378
|
+
id: str = Field(
|
|
379
|
+
..., description="Unique ID of the authorization configuration."
|
|
380
|
+
)
|
|
381
|
+
api_key: str | None = Field(
|
|
382
|
+
default=None, description="API key if using token-based auth."
|
|
383
|
+
)
|
|
384
|
+
client_id: str | None = Field(
|
|
385
|
+
default=None, description="OAuth2 client ID."
|
|
386
|
+
)
|
|
387
|
+
client_secret: str | None = Field(
|
|
388
|
+
default=None, description="OAuth2 client secret."
|
|
389
|
+
)
|
|
390
|
+
host: str | None = Field(
|
|
391
|
+
default=None, description="Base URL or domain of the provider."
|
|
392
|
+
)
|
|
393
|
+
scopes: list[str] | None = Field(
|
|
394
|
+
default=None, description="OAuth2 scopes required."
|
|
395
|
+
)
|
|
396
|
+
token_url: str | None = Field(
|
|
397
|
+
default=None, description="Token endpoint URL."
|
|
398
|
+
)
|
|
399
|
+
type: str = Field(
|
|
400
|
+
..., description="Authorization method, e.g., 'oauth2' or 'api_key'."
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
class TelemetrySink(StrictBaseModel):
|
|
405
|
+
"""Defines an observability endpoint for collecting telemetry data from the QType runtime."""
|
|
406
|
+
|
|
407
|
+
id: str = Field(
|
|
408
|
+
..., description="Unique ID of the telemetry sink configuration."
|
|
409
|
+
)
|
|
410
|
+
auth: AuthorizationProvider | str | None = Field(
|
|
411
|
+
default=None,
|
|
412
|
+
description="AuthorizationProvider used to authenticate telemetry data transmission.",
|
|
413
|
+
)
|
|
414
|
+
endpoint: str = Field(
|
|
415
|
+
..., description="URL endpoint where telemetry data will be sent."
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
#
|
|
420
|
+
# ---------------- Application Definition ----------------
|
|
421
|
+
#
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
class Application(StrictBaseModel):
|
|
425
|
+
"""Defines a QType application that can include models, variables, and other components."""
|
|
426
|
+
|
|
427
|
+
id: str = Field(..., description="Unique ID of the application.")
|
|
428
|
+
description: str | None = Field(
|
|
429
|
+
default=None, description="Optional description of the application."
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
# Core components
|
|
433
|
+
memories: list[Memory] | None = Field(
|
|
434
|
+
default=None,
|
|
435
|
+
description="List of memory definitions used in this application.",
|
|
436
|
+
)
|
|
437
|
+
models: list[ModelType] | None = Field(
|
|
438
|
+
default=None, description="List of models used in this application."
|
|
439
|
+
)
|
|
440
|
+
types: list[TypeDefinition] | None = Field(
|
|
441
|
+
default=None,
|
|
442
|
+
description="List of custom types defined in this application.",
|
|
443
|
+
)
|
|
444
|
+
variables: list[Variable] | None = Field(
|
|
445
|
+
default=None, description="List of variables used in this application."
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Orchestration
|
|
449
|
+
flows: list[Flow] | None = Field(
|
|
450
|
+
default=None, description="List of flows defined in this application."
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
# External integrations
|
|
454
|
+
auths: list[AuthorizationProvider] | None = Field(
|
|
455
|
+
default=None,
|
|
456
|
+
description="List of authorization providers used for API access.",
|
|
457
|
+
)
|
|
458
|
+
tools: list[ToolType] | None = Field(
|
|
459
|
+
default=None,
|
|
460
|
+
description="List of tools available in this application.",
|
|
461
|
+
)
|
|
462
|
+
indexes: list[IndexType] | None = Field(
|
|
463
|
+
default=None,
|
|
464
|
+
description="List of indexes available for search operations.",
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# Observability
|
|
468
|
+
telemetry: TelemetrySink | None = Field(
|
|
469
|
+
default=None, description="Optional telemetry sink for observability."
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
# Extensibility
|
|
473
|
+
references: list[Document] | None = Field(
|
|
474
|
+
default=None,
|
|
475
|
+
description="List of other q-type documents you may use. This allows modular composition and reuse of components across applications.",
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
#
|
|
480
|
+
# ---------------- Retrieval Augmented Generation Components ----------------
|
|
481
|
+
#
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
class Index(StrictBaseModel, ABC):
|
|
485
|
+
"""Base class for searchable indexes that can be queried by search steps."""
|
|
486
|
+
|
|
487
|
+
id: str = Field(..., description="Unique ID of the index.")
|
|
488
|
+
args: dict[str, Any] | None = Field(
|
|
489
|
+
default=None,
|
|
490
|
+
description="Index-specific configuration and connection parameters.",
|
|
491
|
+
)
|
|
492
|
+
auth: AuthorizationProvider | str | None = Field(
|
|
493
|
+
default=None,
|
|
494
|
+
description="AuthorizationProvider for accessing the index.",
|
|
495
|
+
)
|
|
496
|
+
name: str = Field(..., description="Name of the index/collection/table.")
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
class VectorIndex(Index):
|
|
500
|
+
"""Vector database index for similarity search using embeddings."""
|
|
501
|
+
|
|
502
|
+
embedding_model: EmbeddingModel | str = Field(
|
|
503
|
+
...,
|
|
504
|
+
description="Embedding model used to vectorize queries and documents.",
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
class DocumentIndex(Index):
|
|
509
|
+
"""Document search index for text-based search (e.g., Elasticsearch, OpenSearch)."""
|
|
510
|
+
|
|
511
|
+
# TODO: add anything that is needed for document search indexes
|
|
512
|
+
pass
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
class Search(Step, ABC):
|
|
516
|
+
"""Base class for search operations against indexes."""
|
|
517
|
+
|
|
518
|
+
filters: dict[str, Any] | None = Field(
|
|
519
|
+
default=None, description="Optional filters to apply during search."
|
|
520
|
+
)
|
|
521
|
+
index: IndexType | str = Field(
|
|
522
|
+
..., description="Index to search against (object or ID reference)."
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
class VectorSearch(Search):
|
|
527
|
+
"""Performs vector similarity search against a vector index."""
|
|
528
|
+
|
|
529
|
+
default_top_k: int | None = Field(
|
|
530
|
+
description="Number of top results to retrieve if not provided in the inputs.",
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
@model_validator(mode="after")
|
|
534
|
+
def set_default_inputs_outputs(self) -> "VectorSearch":
|
|
535
|
+
"""Set default input and output variables if none provided."""
|
|
536
|
+
if self.inputs is None:
|
|
537
|
+
self.inputs = [
|
|
538
|
+
Variable(id="top_k", type=PrimitiveTypeEnum.number),
|
|
539
|
+
Variable(id="query", type=PrimitiveTypeEnum.text),
|
|
540
|
+
]
|
|
541
|
+
|
|
542
|
+
if self.outputs is None:
|
|
543
|
+
self.outputs = [
|
|
544
|
+
Variable(
|
|
545
|
+
id=f"{self.id}.results",
|
|
546
|
+
type=ArrayTypeDefinition(
|
|
547
|
+
id=f"{self.id}.SearchResult",
|
|
548
|
+
type=ObjectTypeDefinition(
|
|
549
|
+
id="SearchResult",
|
|
550
|
+
description="Result of a search operation.",
|
|
551
|
+
properties={
|
|
552
|
+
"score": PrimitiveTypeEnum.number,
|
|
553
|
+
"id": PrimitiveTypeEnum.text,
|
|
554
|
+
"document": PrimitiveTypeEnum.text,
|
|
555
|
+
},
|
|
556
|
+
),
|
|
557
|
+
description=None,
|
|
558
|
+
),
|
|
559
|
+
)
|
|
560
|
+
]
|
|
561
|
+
return self
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
class DocumentSearch(Search):
|
|
565
|
+
"""Performs document search against a document index."""
|
|
566
|
+
|
|
567
|
+
@model_validator(mode="after")
|
|
568
|
+
def set_default_inputs_outputs(self) -> "DocumentSearch":
|
|
569
|
+
"""Set default input and output variables if none provided."""
|
|
570
|
+
if self.inputs is None:
|
|
571
|
+
self.inputs = [Variable(id="query", type=PrimitiveTypeEnum.text)]
|
|
572
|
+
|
|
573
|
+
if self.outputs is None:
|
|
574
|
+
self.outputs = [
|
|
575
|
+
Variable(id=f"{self.id}.results", type=PrimitiveTypeEnum.text)
|
|
576
|
+
]
|
|
577
|
+
return self
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
# Create a union type for all tool types
|
|
581
|
+
ToolType = Union[
|
|
582
|
+
APITool,
|
|
583
|
+
PythonFunctionTool,
|
|
584
|
+
]
|
|
585
|
+
|
|
586
|
+
# Create a union type for all step types
|
|
587
|
+
StepType = Union[
|
|
588
|
+
Agent,
|
|
589
|
+
APITool,
|
|
590
|
+
Condition,
|
|
591
|
+
Decoder,
|
|
592
|
+
DocumentSearch,
|
|
593
|
+
Flow,
|
|
594
|
+
LLMInference,
|
|
595
|
+
PromptTemplate,
|
|
596
|
+
PythonFunctionTool,
|
|
597
|
+
VectorSearch,
|
|
598
|
+
]
|
|
599
|
+
|
|
600
|
+
# Create a union type for all index types
|
|
601
|
+
IndexType = Union[
|
|
602
|
+
DocumentIndex,
|
|
603
|
+
VectorIndex,
|
|
604
|
+
]
|
|
605
|
+
|
|
606
|
+
# Create a union type for all model types
|
|
607
|
+
ModelType = Union[
|
|
608
|
+
EmbeddingModel,
|
|
609
|
+
Model,
|
|
610
|
+
]
|
|
611
|
+
|
|
612
|
+
#
|
|
613
|
+
# ---------------- Document Flexibility Shapes ----------------
|
|
614
|
+
# The following shapes let users define a set of flexible document structures
|
|
615
|
+
#
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
class AuthorizationProviderList(RootModel[list[AuthorizationProvider]]):
|
|
619
|
+
"""Schema for a standalone list of authorization providers."""
|
|
620
|
+
|
|
621
|
+
root: list[AuthorizationProvider]
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
class IndexList(RootModel[list[IndexType]]):
|
|
625
|
+
"""Schema for a standalone list of indexes."""
|
|
626
|
+
|
|
627
|
+
root: list[IndexType]
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
class ModelList(RootModel[list[ModelType]]):
|
|
631
|
+
"""Schema for a standalone list of models."""
|
|
632
|
+
|
|
633
|
+
root: list[ModelType]
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
class ToolList(RootModel[list[ToolType]]):
|
|
637
|
+
"""Schema for a standalone list of tools."""
|
|
638
|
+
|
|
639
|
+
root: list[ToolType]
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
class TypeList(RootModel[list[TypeDefinition]]):
|
|
643
|
+
"""Schema for a standalone list of type definitions."""
|
|
644
|
+
|
|
645
|
+
root: list[TypeDefinition]
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
class VariableList(RootModel[list[Variable]]):
|
|
649
|
+
"""Schema for a standalone list of variables."""
|
|
650
|
+
|
|
651
|
+
root: list[Variable]
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
class Document(
|
|
655
|
+
RootModel[
|
|
656
|
+
Union[
|
|
657
|
+
Agent,
|
|
658
|
+
Application,
|
|
659
|
+
AuthorizationProviderList,
|
|
660
|
+
Flow,
|
|
661
|
+
IndexList,
|
|
662
|
+
ModelList,
|
|
663
|
+
ToolList,
|
|
664
|
+
TypeList,
|
|
665
|
+
VariableList,
|
|
666
|
+
]
|
|
667
|
+
]
|
|
668
|
+
):
|
|
669
|
+
"""Schema for any valid QType document structure.
|
|
670
|
+
|
|
671
|
+
This allows validation of standalone lists of components, individual components,
|
|
672
|
+
or full QType application specs. Supports modular composition and reuse.
|
|
673
|
+
"""
|
|
674
|
+
|
|
675
|
+
root: Union[
|
|
676
|
+
Agent,
|
|
677
|
+
Application,
|
|
678
|
+
AuthorizationProviderList,
|
|
679
|
+
Flow,
|
|
680
|
+
IndexList,
|
|
681
|
+
ModelList,
|
|
682
|
+
ToolList,
|
|
683
|
+
TypeList,
|
|
684
|
+
VariableList,
|
|
685
|
+
]
|