qtype 0.0.5__py3-none-any.whl → 0.0.7__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 (67) hide show
  1. qtype/commands/convert.py +18 -5
  2. qtype/commands/generate.py +16 -8
  3. qtype/commands/run.py +6 -83
  4. qtype/commands/serve.py +73 -0
  5. qtype/commands/validate.py +18 -8
  6. qtype/commands/visualize.py +87 -0
  7. qtype/commons/generate.py +9 -4
  8. qtype/converters/tools_from_module.py +69 -134
  9. qtype/converters/types.py +47 -1
  10. qtype/dsl/base_types.py +0 -1
  11. qtype/dsl/custom_types.py +73 -0
  12. qtype/dsl/document.py +27 -3
  13. qtype/dsl/domain_types.py +3 -0
  14. qtype/dsl/model.py +60 -73
  15. qtype/dsl/validator.py +20 -0
  16. qtype/interpreter/api.py +45 -12
  17. qtype/interpreter/chat/chat_api.py +237 -0
  18. qtype/interpreter/chat/file_conversions.py +57 -0
  19. qtype/interpreter/chat/vercel.py +314 -0
  20. qtype/interpreter/conversions.py +2 -0
  21. qtype/interpreter/steps/llm_inference.py +44 -19
  22. qtype/interpreter/streaming_helpers.py +123 -0
  23. qtype/interpreter/typing.py +29 -10
  24. qtype/interpreter/ui/404/index.html +1 -0
  25. qtype/interpreter/ui/404.html +1 -0
  26. qtype/interpreter/ui/_next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +1 -0
  27. qtype/interpreter/ui/_next/static/chunks/736-7fc606e244fedcb1.js +36 -0
  28. qtype/interpreter/ui/_next/static/chunks/964-ed4ab073db645007.js +1 -0
  29. qtype/interpreter/ui/_next/static/chunks/app/_not-found/page-e110d2a9d0a83d82.js +1 -0
  30. qtype/interpreter/ui/_next/static/chunks/app/layout-f8f02d19bf177f87.js +1 -0
  31. qtype/interpreter/ui/_next/static/chunks/app/page-c72e847e888e549d.js +1 -0
  32. qtype/interpreter/ui/_next/static/chunks/ba12c10f-22556063851a6df2.js +1 -0
  33. qtype/interpreter/ui/_next/static/chunks/framework-7c95b8e5103c9e90.js +1 -0
  34. qtype/interpreter/ui/_next/static/chunks/main-6d261b6c5d6fb6c2.js +1 -0
  35. qtype/interpreter/ui/_next/static/chunks/main-app-6fc6346bc8f7f163.js +1 -0
  36. qtype/interpreter/ui/_next/static/chunks/pages/_app-0a0020ddd67f79cf.js +1 -0
  37. qtype/interpreter/ui/_next/static/chunks/pages/_error-03529f2c21436739.js +1 -0
  38. qtype/interpreter/ui/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  39. qtype/interpreter/ui/_next/static/chunks/webpack-ebad980006f664ca.js +1 -0
  40. qtype/interpreter/ui/_next/static/css/cd7a636f069c2cbd.css +3 -0
  41. qtype/interpreter/ui/_next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  42. qtype/interpreter/ui/_next/static/media/747892c23ea88013-s.woff2 +0 -0
  43. qtype/interpreter/ui/_next/static/media/8d697b304b401681-s.woff2 +0 -0
  44. qtype/interpreter/ui/_next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  45. qtype/interpreter/ui/_next/static/media/9610d9e46709d722-s.woff2 +0 -0
  46. qtype/interpreter/ui/_next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  47. qtype/interpreter/ui/_next/static/qlZrsNT9fTEe92CsYpHBB/_buildManifest.js +1 -0
  48. qtype/interpreter/ui/_next/static/qlZrsNT9fTEe92CsYpHBB/_ssgManifest.js +1 -0
  49. qtype/interpreter/ui/favicon.ico +0 -0
  50. qtype/interpreter/ui/file.svg +1 -0
  51. qtype/interpreter/ui/globe.svg +1 -0
  52. qtype/interpreter/ui/index.html +1 -0
  53. qtype/interpreter/ui/index.txt +22 -0
  54. qtype/interpreter/ui/next.svg +1 -0
  55. qtype/interpreter/ui/vercel.svg +1 -0
  56. qtype/interpreter/ui/window.svg +1 -0
  57. qtype/loader.py +57 -8
  58. qtype/semantic/generate.py +17 -5
  59. qtype/semantic/model.py +16 -24
  60. qtype/semantic/visualize.py +485 -0
  61. {qtype-0.0.5.dist-info → qtype-0.0.7.dist-info}/METADATA +28 -20
  62. qtype-0.0.7.dist-info/RECORD +91 -0
  63. qtype-0.0.5.dist-info/RECORD +0 -50
  64. {qtype-0.0.5.dist-info → qtype-0.0.7.dist-info}/WHEEL +0 -0
  65. {qtype-0.0.5.dist-info → qtype-0.0.7.dist-info}/entry_points.txt +0 -0
  66. {qtype-0.0.5.dist-info → qtype-0.0.7.dist-info}/licenses/LICENSE +0 -0
  67. {qtype-0.0.5.dist-info → qtype-0.0.7.dist-info}/top_level.txt +0 -0
qtype/commands/convert.py CHANGED
@@ -3,9 +3,7 @@ import logging
3
3
 
4
4
  from pydantic_yaml import to_yaml_str
5
5
 
6
- from qtype.commons.generate import _write_yaml_file
7
- from qtype.converters.tools_from_module import tools_from_module
8
- from qtype.dsl.model import ToolList
6
+ from qtype.dsl.model import Application
9
7
 
10
8
  logger = logging.getLogger(__name__)
11
9
 
@@ -16,12 +14,27 @@ def convert_api(args: argparse.Namespace) -> None:
16
14
 
17
15
  def convert_module(args: argparse.Namespace) -> None:
18
16
  """Convert Python module tools to qtype format."""
19
- tools = ToolList(tools_from_module(args.module_path)) # type: ignore
17
+
18
+ from qtype.commons.generate import _write_yaml_file
19
+ from qtype.converters.tools_from_module import tools_from_module
20
+ from qtype.dsl.model import ToolList
21
+
22
+ tools, types = tools_from_module(args.module_path) # type: ignore
20
23
  if not tools:
21
24
  raise ValueError(f"No tools found in the module: {args.module_path}")
25
+
26
+ if types:
27
+ doc = Application(
28
+ id=args.module_path,
29
+ description=f"Tools created from Python module {args.module_path}",
30
+ tools=list(tools),
31
+ types=types,
32
+ )
33
+ else:
34
+ doc = ToolList(root=list(tools))
22
35
 
23
36
  if args.output:
24
- _write_yaml_file(tools, args.output)
37
+ _write_yaml_file(doc, args.output)
25
38
  logger.info("Resulting yaml written to %s", args.output)
26
39
  else:
27
40
  logger.info(
@@ -4,13 +4,23 @@ import logging
4
4
  from pathlib import Path
5
5
  from typing import Optional
6
6
 
7
- from qtype.commons.generate import dump_commons_library
8
- from qtype.dsl.document import generate_documentation
9
7
  from qtype.dsl.model import Document
10
8
 
11
9
  logger = logging.getLogger(__name__)
12
10
 
13
11
 
12
+ def run_dump_commons_library(args: argparse.Namespace) -> None:
13
+ from qtype.commons.generate import dump_commons_library
14
+
15
+ dump_commons_library(args)
16
+
17
+
18
+ def run_generate_documentation(args: argparse.Namespace) -> None:
19
+ from qtype.dsl.document import generate_documentation
20
+
21
+ generate_documentation(Path(args.output))
22
+
23
+
14
24
  def generate_schema(args: argparse.Namespace) -> None:
15
25
  """Generate and output the JSON schema for Document.
16
26
 
@@ -51,7 +61,7 @@ def parser(subparsers: argparse._SubParsersAction) -> None:
51
61
  default="./common/",
52
62
  help="Output prefix for the YAML file (default: ./common/)",
53
63
  )
54
- commons_parser.set_defaults(func=dump_commons_library)
64
+ commons_parser.set_defaults(func=run_dump_commons_library)
55
65
 
56
66
  # Parser for generating the json schema
57
67
  schema_parser = generate_subparsers.add_parser(
@@ -74,12 +84,10 @@ def parser(subparsers: argparse._SubParsersAction) -> None:
74
84
  "-o",
75
85
  "--output",
76
86
  type=str,
77
- default="docs/DSL-Reference/",
78
- help="Output directory for the DSL documentation (default: docs/DSL-Reference/)",
79
- )
80
- dsl_parser.set_defaults(
81
- func=lambda args: generate_documentation(Path(args.output))
87
+ default="docs/components/",
88
+ help="Output directory for the DSL documentation (default: docs/components/)",
82
89
  )
90
+ dsl_parser.set_defaults(func=run_generate_documentation)
83
91
 
84
92
  # Parser for generating the semantic model
85
93
  # only add this if networkx and ruff are installed
qtype/commands/run.py CHANGED
@@ -47,46 +47,13 @@ def _telemetry(spec: Application) -> None:
47
47
  register(spec.telemetry, spec.id)
48
48
 
49
49
 
50
- def run_api(args: Any) -> None:
51
- """Run a QType YAML spec file as an API.
52
-
53
- Args:
54
- args: Arguments passed from the command line or calling context.
55
- """
56
- spec = load(args.spec)
57
- logger.info(f"Running API for spec: {args.spec}")
58
- from qtype.interpreter.api import APIExecutor
59
-
60
- # Get the name from the spec filename.
61
- # so if filename is tests/specs/full_application_test.qtype.yaml, name should be "Full Application Test"
62
- name = (
63
- args.spec.split("/")[-1]
64
- .replace(".qtype.yaml", "")
65
- .replace("_", " ")
66
- .title()
67
- )
68
-
69
- _telemetry(spec)
70
- api_executor = APIExecutor(spec)
71
- fastapi_app = api_executor.create_app(name=name)
72
-
73
- import uvicorn
74
-
75
- uvicorn.run(
76
- fastapi_app,
77
- host=args.host,
78
- port=args.port,
79
- log_level="info",
80
- )
81
-
82
-
83
50
  def run_flow(args: Any) -> None:
84
51
  """Run a QType YAML spec file by executing its flows.
85
52
 
86
53
  Args:
87
54
  args: Arguments passed from the command line or calling context.
88
55
  """
89
- spec = load(args.spec)
56
+ spec, _ = load(args.spec)
90
57
 
91
58
  flow = _get_flow(spec, args.flow)
92
59
  logger.info(f"Executing flow: {flow.id}")
@@ -127,17 +94,6 @@ def run_flow(args: Any) -> None:
127
94
  print("\n")
128
95
 
129
96
 
130
- def run_ui(args: Any) -> None:
131
- """Run a QType YAML spec file by executing its flows in a UI.
132
-
133
- Args:
134
- args: Arguments passed from the command line or calling context.
135
- """
136
- # Placeholder for actual implementation
137
- logger.info(f"Running UI for spec: {args.spec}")
138
- # Here you would implement the logic to run the flow in a UI context
139
-
140
-
141
97
  def parser(subparsers: argparse._SubParsersAction) -> None:
142
98
  """Set up the run subcommand parser.
143
99
 
@@ -145,56 +101,23 @@ def parser(subparsers: argparse._SubParsersAction) -> None:
145
101
  subparsers: The subparsers object to add the command to.
146
102
  """
147
103
  cmd_parser = subparsers.add_parser(
148
- "run", help="Run a QType YAML spec by executing its flows."
104
+ "run", help="Executes a QType Application locally"
149
105
  )
150
-
151
- run_subparsers = cmd_parser.add_subparsers(
152
- dest="run_method", required=True
153
- )
154
-
155
- # Parse for generating API runner
156
- api_runner_parser = run_subparsers.add_parser(
157
- "api", help="Serves the qtype file as an API."
158
- )
159
- api_runner_parser.add_argument(
160
- "-H", "--host", type=str, default="localhost"
161
- )
162
- api_runner_parser.add_argument("-p", "--port", type=int, default=8000)
163
- api_runner_parser.set_defaults(func=run_api)
164
-
165
- # Parse for running a flow
166
- flow_parser = run_subparsers.add_parser(
167
- "flow", help="Runs a QType YAML spec file by executing its flows."
168
- )
169
- flow_parser.add_argument(
106
+ cmd_parser.add_argument(
170
107
  "-f",
171
108
  "--flow",
172
109
  type=str,
173
110
  default=None,
174
111
  help="The name of the flow to run. If not specified, runs the first flow found.",
175
112
  )
176
- flow_parser.add_argument(
113
+ cmd_parser.add_argument(
177
114
  "input",
178
115
  type=str,
179
116
  help="JSON blob of input values for the flow.",
180
117
  )
181
118
 
182
- flow_parser.set_defaults(func=run_flow)
183
-
184
- # Run a user interface for the spec
185
- ui_parser = run_subparsers.add_parser(
186
- "ui",
187
- help="Runs a QType YAML spec file by executing its flows in a UI.",
188
- )
189
- ui_parser.add_argument(
190
- "-f",
191
- "--flow",
192
- type=str,
193
- default=None,
194
- help="The name of the flow to run in the UI. If not specified, runs the first flow found.",
195
- )
196
- ui_parser.set_defaults(func=run_ui)
197
-
198
119
  cmd_parser.add_argument(
199
120
  "spec", type=str, help="Path to the QType YAML spec file."
200
121
  )
122
+
123
+ cmd_parser.set_defaults(func=run_flow)
@@ -0,0 +1,73 @@
1
+ """
2
+ Command-line interface for running QType YAML spec files.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import argparse
8
+ import logging
9
+ from typing import Any
10
+
11
+ import uvicorn
12
+
13
+ from qtype.commands.run import _telemetry
14
+ from qtype.loader import load
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def serve(args: Any) -> None:
20
+ """Run a QType YAML spec file as an API.
21
+
22
+ Args:
23
+ args: Arguments passed from the command line or calling context.
24
+ """
25
+ spec, _ = load(args.spec)
26
+ logger.info(f"Running API for spec: {args.spec}")
27
+ from qtype.interpreter.api import APIExecutor
28
+
29
+ # Get the name from the spec filename.
30
+ # so if filename is tests/specs/full_application_test.qtype.yaml, name should be "Full Application Test"
31
+ name = (
32
+ args.spec.split("/")[-1]
33
+ .replace(".qtype.yaml", "")
34
+ .replace("_", " ")
35
+ .title()
36
+ )
37
+
38
+ _telemetry(spec)
39
+ api_executor = APIExecutor(spec)
40
+ fastapi_app = api_executor.create_app(
41
+ name=name, ui_enabled=not args.disable_ui
42
+ )
43
+
44
+ uvicorn.run(
45
+ fastapi_app,
46
+ host=args.host,
47
+ port=args.port,
48
+ log_level="info",
49
+ )
50
+
51
+
52
+ def parser(subparsers: argparse._SubParsersAction) -> None:
53
+ """Set up the run subcommand parser.
54
+
55
+ Args:
56
+ subparsers: The subparsers object to add the command to.
57
+ """
58
+ cmd_parser = subparsers.add_parser(
59
+ "serve", help="Serve a web experience for a QType application"
60
+ )
61
+
62
+ cmd_parser.add_argument("-p", "--port", type=int, default=8000)
63
+ cmd_parser.add_argument("-H", "--host", type=str, default="localhost")
64
+ cmd_parser.add_argument(
65
+ "--disable-ui",
66
+ action="store_true",
67
+ help="Disable the UI for the QType application.",
68
+ )
69
+ cmd_parser.set_defaults(func=serve)
70
+
71
+ cmd_parser.add_argument(
72
+ "spec", type=str, help="Path to the QType YAML spec file."
73
+ )
@@ -10,8 +10,13 @@ from typing import Any
10
10
  from pydantic import ValidationError
11
11
 
12
12
  from qtype import dsl
13
+ from qtype.dsl.custom_types import build_dynamic_types
13
14
  from qtype.dsl.validator import QTypeValidationError, validate
14
- from qtype.loader import _resolve_root, load_yaml
15
+ from qtype.loader import (
16
+ _list_dynamic_types_from_document,
17
+ _resolve_root,
18
+ load_yaml,
19
+ )
15
20
  from qtype.semantic.errors import SemanticResolutionError
16
21
  from qtype.semantic.resolver import resolve
17
22
 
@@ -30,29 +35,34 @@ def main(args: Any) -> None:
30
35
  """
31
36
  try:
32
37
  yaml_data = load_yaml(args.spec)
38
+ dynamic_types_lists = _list_dynamic_types_from_document(yaml_data)
39
+ dynamic_types_registry = build_dynamic_types(dynamic_types_lists)
33
40
  logging.info("✅ Schema validation successful.")
34
- document = dsl.Document.model_validate(yaml_data)
41
+
42
+ document = dsl.Document.model_validate(
43
+ yaml_data, context={"custom_types": dynamic_types_registry}
44
+ )
35
45
  logging.info("✅ Model validation successful.")
36
- document = _resolve_root(document)
37
- if not isinstance(document, dsl.Application):
46
+ root = _resolve_root(document)
47
+ if not isinstance(root, dsl.Application):
38
48
  logging.warning(
39
49
  "🟨 Spec is not an Application, skipping semantic resolution."
40
50
  )
41
51
  else:
42
- document = validate(document)
52
+ root = validate(root)
43
53
  logger.info("✅ Language validation successful")
44
- document = resolve(document)
54
+ app = resolve(root)
45
55
  logger.info("✅ Semantic validation successful")
46
56
  if args.print:
47
57
  logger.info(
48
- document.model_dump_json( # type: ignore
58
+ (app if "app" in locals() else root).model_dump_json( # type: ignore
49
59
  indent=2,
50
60
  exclude_none=True,
51
61
  )
52
62
  )
53
63
 
54
64
  except ValidationError as exc:
55
- logger.error("❌ Schema validation failed:\n%s", exc)
65
+ logger.error("❌ Validation failed:\n%s", exc)
56
66
  sys.exit(1)
57
67
  except QTypeValidationError as exc:
58
68
  logger.error("❌ DSL validation failed:\n%s", exc)
@@ -0,0 +1,87 @@
1
+ """
2
+ Command-line interface for visualizing QType YAML spec files.
3
+ """
4
+
5
+ import argparse
6
+ import logging
7
+ import tempfile
8
+ import webbrowser
9
+ from typing import Any
10
+
11
+ from qtype.loader import load
12
+ from qtype.semantic.visualize import visualize_application
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def main(args: Any) -> None:
18
+ """
19
+ visualize a QType YAML spec file against the QTypeSpec schema and semantics.
20
+
21
+ Args:
22
+ args: Arguments passed from the command line or calling context.
23
+
24
+ Exits:
25
+ Exits with code 1 if validation fails.
26
+ """
27
+ import mermaid as md
28
+
29
+ application, _ = load(args.spec)
30
+
31
+ diagram = visualize_application(application)
32
+
33
+ render = None
34
+ if args.output:
35
+ if args.output.endswith(".mmd") or args.output.endswith(".mermaid"):
36
+ with open(args.output, "w") as f:
37
+ f.write(diagram)
38
+ logger.info(f"Mermaid diagram written to {args.output}")
39
+ elif args.output.endswith(".svg"):
40
+ render = md.Mermaid(diagram)
41
+ render.to_svg(args.output)
42
+ logger.info(f"SVG diagram written to {args.output}")
43
+
44
+ elif args.output.endswith(".png"):
45
+ render = md.Mermaid(diagram)
46
+ render.to_png(args.output)
47
+ logger.info(f"PNG diagram written to {args.output}")
48
+
49
+ if not args.no_display:
50
+ if not render:
51
+ render = md.Mermaid(diagram)
52
+
53
+ html_content = render._repr_html_()
54
+ # Create a temporary HTML file to display the diagram
55
+ with tempfile.NamedTemporaryFile(
56
+ delete=False, suffix=".html"
57
+ ) as temp_file:
58
+ temp_file.write(html_content.encode("utf-8"))
59
+ webbrowser.open(temp_file.name)
60
+
61
+
62
+ def parser(subparsers: argparse._SubParsersAction) -> None:
63
+ """Set up the visualize subcommand parser.
64
+
65
+ Args:
66
+ subparsers: The subparsers object to add the command to.
67
+ """
68
+ cmd_parser = subparsers.add_parser(
69
+ "visualize", help="Visualize a QType Application."
70
+ )
71
+ cmd_parser.add_argument(
72
+ "spec", type=str, help="Path to the QType YAML file."
73
+ )
74
+ cmd_parser.add_argument(
75
+ "-o",
76
+ "--output",
77
+ type=str,
78
+ default=None,
79
+ help="If provided, write the mermaid diagram to this file.",
80
+ )
81
+ cmd_parser.add_argument(
82
+ "-nd",
83
+ "--no-display",
84
+ action="store_true",
85
+ help="If set don't display the diagram in a browser (default: False).",
86
+ )
87
+ cmd_parser.set_defaults(func=main)
qtype/commons/generate.py CHANGED
@@ -5,7 +5,7 @@ from pydantic import BaseModel
5
5
  from pydantic_yaml import to_yaml_str
6
6
 
7
7
  from qtype.converters.tools_from_module import tools_from_module
8
- from qtype.dsl.model import Model, ModelList, ToolList
8
+ from qtype.dsl.model import Application, Model, ModelList
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
@@ -25,14 +25,19 @@ def _write_yaml_file(data: BaseModel, output_path: str) -> None:
25
25
 
26
26
 
27
27
  def dump_built_in_tools(args: argparse.Namespace) -> None:
28
- tools = tools_from_module("qtype.commons.tools")
28
+ tools, types = tools_from_module("qtype.commons.tools")
29
29
  if not tools:
30
30
  logger.error("No tools found in the commons library.")
31
31
  return
32
32
 
33
- tool_list = ToolList(root=tools) # type: ignore
33
+ doc = Application(
34
+ id="qtype-common-tools",
35
+ description="Common Tools for QType",
36
+ tools=list(tools),
37
+ types=types,
38
+ )
34
39
  output_path = f"{args.prefix}/tools.qtype.yaml"
35
- _write_yaml_file(tool_list, output_path)
40
+ _write_yaml_file(doc, output_path)
36
41
  logging.info(f"Built-in tools exported to {output_path}")
37
42
 
38
43