weftai 0.1.0a1__tar.gz

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 (58) hide show
  1. weftai-0.1.0a1/.gitignore +30 -0
  2. weftai-0.1.0a1/LICENSE +21 -0
  3. weftai-0.1.0a1/PKG-INFO +30 -0
  4. weftai-0.1.0a1/README.md +5 -0
  5. weftai-0.1.0a1/pyproject.toml +41 -0
  6. weftai-0.1.0a1/src/weftai/__init__.py +119 -0
  7. weftai-0.1.0a1/src/weftai/_abort.py +74 -0
  8. weftai-0.1.0a1/src/weftai/adapter/__init__.py +23 -0
  9. weftai-0.1.0a1/src/weftai/adapter/bind.py +99 -0
  10. weftai-0.1.0a1/src/weftai/adapter/catalog/__init__.py +19 -0
  11. weftai-0.1.0a1/src/weftai/adapter/catalog/chinese.py +346 -0
  12. weftai-0.1.0a1/src/weftai/adapter/catalog/local.py +125 -0
  13. weftai-0.1.0a1/src/weftai/adapter/catalog/lookup.py +131 -0
  14. weftai-0.1.0a1/src/weftai/adapter/catalog/western.py +412 -0
  15. weftai-0.1.0a1/src/weftai/adapter/dialects.py +83 -0
  16. weftai-0.1.0a1/src/weftai/adapter/types.py +47 -0
  17. weftai-0.1.0a1/src/weftai/errors.py +129 -0
  18. weftai-0.1.0a1/src/weftai/executor/__init__.py +4 -0
  19. weftai-0.1.0a1/src/weftai/executor/normalize.py +51 -0
  20. weftai-0.1.0a1/src/weftai/executor/pool.py +37 -0
  21. weftai-0.1.0a1/src/weftai/executor/runtime.py +417 -0
  22. weftai-0.1.0a1/src/weftai/executor/signals.py +109 -0
  23. weftai-0.1.0a1/src/weftai/executor/types.py +18 -0
  24. weftai-0.1.0a1/src/weftai/format/__init__.py +12 -0
  25. weftai-0.1.0a1/src/weftai/format/formatter.py +95 -0
  26. weftai-0.1.0a1/src/weftai/format/sanitize.py +36 -0
  27. weftai-0.1.0a1/src/weftai/format/step.py +133 -0
  28. weftai-0.1.0a1/src/weftai/format/tokens.py +16 -0
  29. weftai-0.1.0a1/src/weftai/ids.py +26 -0
  30. weftai-0.1.0a1/src/weftai/operation.py +135 -0
  31. weftai-0.1.0a1/src/weftai/plan/__init__.py +4 -0
  32. weftai-0.1.0a1/src/weftai/plan/types.py +44 -0
  33. weftai-0.1.0a1/src/weftai/plan/validate.py +356 -0
  34. weftai-0.1.0a1/src/weftai/py.typed +0 -0
  35. weftai-0.1.0a1/src/weftai/refs/__init__.py +3 -0
  36. weftai-0.1.0a1/src/weftai/refs/syntax.py +75 -0
  37. weftai-0.1.0a1/src/weftai/registry/__init__.py +74 -0
  38. weftai-0.1.0a1/src/weftai/registry/describe.py +65 -0
  39. weftai-0.1.0a1/src/weftai/registry/plan_schema.py +84 -0
  40. weftai-0.1.0a1/src/weftai/results/__init__.py +12 -0
  41. weftai-0.1.0a1/src/weftai/results/resolve.py +39 -0
  42. weftai-0.1.0a1/src/weftai/results/session.py +25 -0
  43. weftai-0.1.0a1/src/weftai/results/store.py +131 -0
  44. weftai-0.1.0a1/src/weftai/results/types.py +41 -0
  45. weftai-0.1.0a1/src/weftai/schema/__init__.py +29 -0
  46. weftai-0.1.0a1/src/weftai/schema/json.py +109 -0
  47. weftai-0.1.0a1/src/weftai/schema/ref.py +53 -0
  48. weftai-0.1.0a1/src/weftai/schema/spec.py +355 -0
  49. weftai-0.1.0a1/src/weftai/schema/summarize.py +107 -0
  50. weftai-0.1.0a1/src/weftai/schema/types.py +160 -0
  51. weftai-0.1.0a1/src/weftai/schema/walk.py +140 -0
  52. weftai-0.1.0a1/src/weftai/std/__init__.py +12 -0
  53. weftai-0.1.0a1/src/weftai/std/fields.py +39 -0
  54. weftai-0.1.0a1/src/weftai/std/match.py +119 -0
  55. weftai-0.1.0a1/src/weftai/std/operations.py +310 -0
  56. weftai-0.1.0a1/src/weftai/trace.py +56 -0
  57. weftai-0.1.0a1/src/weftai/util/__init__.py +3 -0
  58. weftai-0.1.0a1/src/weftai/util/levenshtein.py +56 -0
@@ -0,0 +1,30 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ .venv/
7
+ venv/
8
+ dist/
9
+ build/
10
+ *.egg-info/
11
+ .eggs/
12
+ .coverage
13
+ .coverage.*
14
+ htmlcov/
15
+ .pytest_cache/
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+ .pyright/
19
+ .tox/
20
+ .env
21
+ .env.*
22
+ !.env.example
23
+ uv.lock.bak
24
+ .DS_Store
25
+ Thumbs.db
26
+ .idea/
27
+ .vscode/
28
+ *.tsbuildinfo
29
+ .coverage.*
30
+ htmlcov/
weftai-0.1.0a1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rex Technologies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,30 @@
1
+ Metadata-Version: 2.5
2
+ Name: weftai
3
+ Version: 0.1.0a1
4
+ Summary: Weftai: composable AI workflows (Rex Technologies)
5
+ Project-URL: Homepage, https://github.com/tochi-mba/weftai-python
6
+ Project-URL: Documentation, https://github.com/tochi-mba/weftai-python/tree/main/docs
7
+ Project-URL: Source, https://github.com/tochi-mba/weftai-python
8
+ Author: Rex Technologies
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,ai,llm,tools,workflows
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: pydantic<3,>=2.10
19
+ Provides-Extra: all
20
+ Requires-Dist: weftai-cli; extra == 'all'
21
+ Requires-Dist: weftai-mcp; extra == 'all'
22
+ Requires-Dist: weftai-providers; extra == 'all'
23
+ Requires-Dist: weftai-testing; extra == 'all'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # weftai
27
+
28
+ Core runtime and adapter catalog. Install: `pip install weftai`.
29
+
30
+ See the [workspace README](https://github.com/tochi-mba/weftai-python).
@@ -0,0 +1,5 @@
1
+ # weftai
2
+
3
+ Core runtime and adapter catalog. Install: `pip install weftai`.
4
+
5
+ See the [workspace README](https://github.com/tochi-mba/weftai-python).
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["hatchling", "hatch-cada>=1.0.3"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "weftai"
7
+ version = "0.1.0a1"
8
+ description = "Weftai: composable AI workflows (Rex Technologies)"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.12"
12
+ authors = [{ name = "Rex Technologies" }]
13
+ keywords = ["ai", "agents", "workflows", "llm", "tools"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Typing :: Typed",
20
+ ]
21
+ dependencies = ["pydantic>=2.10,<3"]
22
+
23
+ [project.optional-dependencies]
24
+ all = ["weftai-providers", "weftai-mcp", "weftai-cli", "weftai-testing"]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/tochi-mba/weftai-python"
28
+ Documentation = "https://github.com/tochi-mba/weftai-python/tree/main/docs"
29
+ Source = "https://github.com/tochi-mba/weftai-python"
30
+
31
+ [tool.hatch.metadata.hooks.cada]
32
+ strategy = "pin"
33
+
34
+ [tool.hatch.build]
35
+ dev-mode-dirs = ["src"]
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/weftai"]
39
+
40
+ [tool.hatch.build.targets.sdist]
41
+ include = ["src/weftai", "README.md", "LICENSE"]
@@ -0,0 +1,119 @@
1
+ """Weftai: composable AI workflows. A Rex Technologies product."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pkgutil
6
+
7
+ __path__ = pkgutil.extend_path(__path__, __name__)
8
+
9
+ from pydantic import BaseModel, Field, TypeAdapter, ValidationError
10
+
11
+ from weftai.errors import (
12
+ DefinitionError,
13
+ LimitExceededError,
14
+ PlanValidationError,
15
+ RefResolutionError,
16
+ RegistryError,
17
+ StepExecutionError,
18
+ WeftaiError,
19
+ format_issue,
20
+ format_issues,
21
+ )
22
+ from weftai.executor.runtime import create_runtime
23
+ from weftai.executor.types import DEFAULT_LIMITS, TRACE_VERSION
24
+ from weftai.format.formatter import create_formatter
25
+ from weftai.format.sanitize import format_property, sanitize_label
26
+ from weftai.format.tokens import DEFAULT_BUDGETS, estimate_tokens
27
+ from weftai.ids import OPERATION_NAME_PATTERN, STEP_ID_PATTERN, is_valid_operation_name, is_valid_step_id
28
+ from weftai.operation import define_operation, define_operation_for, provenance_type
29
+ from weftai.plan.types import PlanSchema, PlanStepSchema
30
+ from weftai.plan.validate import validate_plan
31
+ from weftai.refs.syntax import REF_PATTERN_SOURCE, format_ref, parse_ref
32
+ from weftai.registry import create_registry
33
+ from weftai.registry.describe import describe_operations
34
+ from weftai.registry.plan_schema import build_plan_schema
35
+ from weftai.results.resolve import resolve_ref
36
+ from weftai.results.session import session_view
37
+ from weftai.results.store import create_memory_store
38
+ from weftai.results.types import DEFAULT_SESSION_ID, DEFAULT_STORE_LIMITS
39
+ from weftai.schema.json import input_json_schema
40
+ from weftai.schema.ref import is_ref_schema, ref, ref_meta
41
+ from weftai.schema.summarize import summarize_schema
42
+ from weftai.schema.types import (
43
+ collection,
44
+ groups,
45
+ is_step_output,
46
+ make_collection,
47
+ value,
48
+ with_sources,
49
+ )
50
+ from weftai.std.fields import field_names, resolve_field
51
+ from weftai.std.match import FILTER_OPS, matches_filter
52
+ from weftai.std.operations import STANDARD_OP_KINDS, standard_operations
53
+ from weftai.trace import build_trace
54
+
55
+ VERSION = "0.1.0"
56
+
57
+ __all__ = [
58
+ "BaseModel",
59
+ "DEFAULT_BUDGETS",
60
+ "DEFAULT_LIMITS",
61
+ "DEFAULT_SESSION_ID",
62
+ "DEFAULT_STORE_LIMITS",
63
+ "DefinitionError",
64
+ "FILTER_OPS",
65
+ "Field",
66
+ "LimitExceededError",
67
+ "OPERATION_NAME_PATTERN",
68
+ "PlanSchema",
69
+ "PlanStepSchema",
70
+ "PlanValidationError",
71
+ "REF_PATTERN_SOURCE",
72
+ "RefResolutionError",
73
+ "RegistryError",
74
+ "STANDARD_OP_KINDS",
75
+ "STEP_ID_PATTERN",
76
+ "StepExecutionError",
77
+ "TRACE_VERSION",
78
+ "TypeAdapter",
79
+ "VERSION",
80
+ "ValidationError",
81
+ "WeftaiError",
82
+ "build_plan_schema",
83
+ "build_trace",
84
+ "collection",
85
+ "create_formatter",
86
+ "create_memory_store",
87
+ "create_registry",
88
+ "create_runtime",
89
+ "define_operation",
90
+ "define_operation_for",
91
+ "describe_operations",
92
+ "estimate_tokens",
93
+ "field_names",
94
+ "format_issue",
95
+ "format_issues",
96
+ "format_property",
97
+ "format_ref",
98
+ "groups",
99
+ "input_json_schema",
100
+ "is_ref_schema",
101
+ "is_step_output",
102
+ "is_valid_operation_name",
103
+ "is_valid_step_id",
104
+ "make_collection",
105
+ "matches_filter",
106
+ "parse_ref",
107
+ "provenance_type",
108
+ "ref",
109
+ "ref_meta",
110
+ "resolve_field",
111
+ "resolve_ref",
112
+ "sanitize_label",
113
+ "session_view",
114
+ "standard_operations",
115
+ "summarize_schema",
116
+ "validate_plan",
117
+ "value",
118
+ "with_sources",
119
+ ]
@@ -0,0 +1,74 @@
1
+ """AbortController / AbortSignal, matching the DOM API the TypeScript runtime uses."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from typing import Any
7
+
8
+
9
+ class AbortError(Exception):
10
+ """Raised or stored as `signal.reason` when a step or plan is cancelled."""
11
+
12
+ def __init__(self, message: str = "This operation was aborted") -> None:
13
+ super().__init__(message)
14
+ self.name = "AbortError"
15
+
16
+
17
+ class AbortSignal:
18
+ def __init__(self) -> None:
19
+ self._aborted = False
20
+ self._reason: object = None
21
+ self._listeners: list[tuple[Callable[[], None], bool]] = []
22
+
23
+ @property
24
+ def aborted(self) -> bool:
25
+ return self._aborted
26
+
27
+ @property
28
+ def reason(self) -> object:
29
+ return self._reason
30
+
31
+ def add_event_listener(
32
+ self,
33
+ event: str,
34
+ callback: Callable[[], None],
35
+ *,
36
+ once: bool = False,
37
+ ) -> None:
38
+ if event != "abort":
39
+ return
40
+ if self._aborted:
41
+ callback()
42
+ return
43
+ self._listeners.append((callback, once))
44
+
45
+ def remove_event_listener(self, event: str, callback: Callable[[], None]) -> None:
46
+ if event != "abort":
47
+ return
48
+ self._listeners = [(cb, once) for cb, once in self._listeners if cb is not callback]
49
+
50
+ def _abort(self, reason: object) -> None:
51
+ if self._aborted:
52
+ return
53
+ self._aborted = True
54
+ self._reason = reason if reason is not None else AbortError()
55
+ listeners = list(self._listeners)
56
+ self._listeners.clear()
57
+ for callback, _once in listeners:
58
+ callback()
59
+
60
+
61
+ class AbortController:
62
+ def __init__(self) -> None:
63
+ self.signal = AbortSignal()
64
+
65
+ def abort(self, reason: object = None) -> None:
66
+ self.signal._abort(reason)
67
+
68
+
69
+ def is_abort_like(error: object) -> bool:
70
+ if isinstance(error, AbortError):
71
+ return True
72
+ if isinstance(error, BaseException) and getattr(error, "name", None) == "AbortError":
73
+ return True
74
+ return False
@@ -0,0 +1,23 @@
1
+ from weftai.adapter.bind import bind_tool, parse_tool_arguments
2
+ from weftai.adapter.catalog import (
3
+ CAPABILITIES,
4
+ CHINESE_CAPABILITIES,
5
+ LOCAL_CAPABILITIES,
6
+ WESTERN_CAPABILITIES,
7
+ lookup_capability,
8
+ require_capability,
9
+ )
10
+ from weftai.adapter.dialects import compile_plan_schema, contains_any_of
11
+
12
+ __all__ = [
13
+ "CAPABILITIES",
14
+ "CHINESE_CAPABILITIES",
15
+ "LOCAL_CAPABILITIES",
16
+ "WESTERN_CAPABILITIES",
17
+ "bind_tool",
18
+ "compile_plan_schema",
19
+ "contains_any_of",
20
+ "lookup_capability",
21
+ "parse_tool_arguments",
22
+ "require_capability",
23
+ ]
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import json
5
+ from typing import Any
6
+
7
+ from weftai.adapter.dialects import compile_plan_schema
8
+ from weftai.plan.types import PlanSchema
9
+
10
+
11
+ async def resolve_ctx(ctx: Any) -> Any:
12
+ if callable(ctx):
13
+ result = ctx()
14
+ if inspect.isawaitable(result):
15
+ return await result
16
+ return result
17
+ return ctx
18
+
19
+
20
+ def bind_tool(runtime: Any, spec: dict[str, Any], context: dict[str, Any]) -> dict[str, Any]:
21
+ include = spec.get("include")
22
+ scoped = runtime.registry.filter(include or (lambda _op: True))
23
+ allow_writes = spec.get("allowWrites")
24
+ if allow_writes is None:
25
+ allow_writes = any(op.effects == "write" for op in scoped.operations)
26
+ dialect = spec.get("dialect") or "union"
27
+ schema = compile_plan_schema(scoped.operations, dialect, {"strict": spec.get("strict")})
28
+ description = spec.get("description") or scoped.describe()
29
+ on_invalid = spec.get("onInvalid") or "text"
30
+
31
+ async def execute(input: object) -> dict[str, Any]:
32
+ parsed = PlanSchema.safe_parse(input)
33
+ if not parsed.success:
34
+ text = format_invalid_plan([{"path": i.path, "message": i.message} for i in parsed.issues])
35
+ if on_invalid == "throw":
36
+ raise ValueError(text)
37
+ return {"text": text, "ok": False}
38
+ result = await runtime.execute(
39
+ parsed.data,
40
+ {
41
+ "ctx": await resolve_ctx(context["ctx"]),
42
+ "session": {"id": context["sessionId"]},
43
+ "allowWrites": allow_writes,
44
+ "include": spec.get("include"),
45
+ },
46
+ )
47
+ return {"text": result["text"], "ok": result["ok"]}
48
+
49
+ async def handle(input: object) -> str:
50
+ return (await execute(input))["text"]
51
+
52
+ return {
53
+ "name": spec["name"],
54
+ "description": description,
55
+ "schema": schema,
56
+ "dialect": dialect,
57
+ "execute": execute,
58
+ "handle": handle,
59
+ }
60
+
61
+
62
+ def format_invalid_plan(issues: list[dict[str, Any]]) -> str:
63
+ if len(issues) == 0:
64
+ return "Could not read this plan: the value is not a plan."
65
+ lines = []
66
+ for issue in issues:
67
+ path = ".".join(str(p) for p in issue.get("path") or ())
68
+ where = "the plan" if path == "" else f"'{path}'"
69
+ lines.append(f"{where}: {issue['message']}")
70
+ return "Could not read this plan. " + " ".join(lines)
71
+
72
+
73
+ def parse_tool_arguments(raw: object) -> dict[str, object]:
74
+ if raw is None:
75
+ return {"ok": True, "value": {}}
76
+ if isinstance(raw, dict | list):
77
+ return {"ok": True, "value": raw}
78
+ if isinstance(raw, bool):
79
+ return {
80
+ "ok": False,
81
+ "message": "Tool arguments must be a JSON object or a JSON string, not boolean.",
82
+ }
83
+ if isinstance(raw, int | float):
84
+ return {
85
+ "ok": False,
86
+ "message": "Tool arguments must be a JSON object or a JSON string, not number.",
87
+ }
88
+ if not isinstance(raw, str):
89
+ return {
90
+ "ok": False,
91
+ "message": f"Tool arguments must be a JSON object or a JSON string, not {type(raw).__name__}.",
92
+ }
93
+ trimmed = raw.strip()
94
+ if len(trimmed) == 0:
95
+ return {"ok": True, "value": {}}
96
+ try:
97
+ return {"ok": True, "value": json.loads(trimmed)}
98
+ except json.JSONDecodeError:
99
+ return {"ok": False, "message": f"Tool arguments are not valid JSON: {trimmed[:80]}."}
@@ -0,0 +1,19 @@
1
+ from weftai.adapter.catalog.chinese import CHINESE_CAPABILITIES
2
+ from weftai.adapter.catalog.local import LOCAL_CAPABILITIES
3
+ from weftai.adapter.catalog.lookup import (
4
+ CAPABILITIES,
5
+ capabilities_for,
6
+ lookup_capability,
7
+ require_capability,
8
+ )
9
+ from weftai.adapter.catalog.western import WESTERN_CAPABILITIES
10
+
11
+ __all__ = [
12
+ "CAPABILITIES",
13
+ "CHINESE_CAPABILITIES",
14
+ "LOCAL_CAPABILITIES",
15
+ "WESTERN_CAPABILITIES",
16
+ "capabilities_for",
17
+ "lookup_capability",
18
+ "require_capability",
19
+ ]