tactus 0.38.0__py3-none-any.whl → 0.39.0__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.
- tactus/__init__.py +1 -1
- tactus/core/dsl_stubs.py +11 -0
- tactus/core/runtime.py +57 -5
- tactus/plugins/__init__.py +3 -0
- tactus/plugins/noaa.py +76 -0
- tactus/stdlib/biblicus/text.py +61 -42
- tactus/validation/semantic_visitor.py +120 -129
- {tactus-0.38.0.dist-info → tactus-0.39.0.dist-info}/METADATA +1 -1
- {tactus-0.38.0.dist-info → tactus-0.39.0.dist-info}/RECORD +12 -10
- {tactus-0.38.0.dist-info → tactus-0.39.0.dist-info}/WHEEL +0 -0
- {tactus-0.38.0.dist-info → tactus-0.39.0.dist-info}/entry_points.txt +0 -0
- {tactus-0.38.0.dist-info → tactus-0.39.0.dist-info}/licenses/LICENSE +0 -0
tactus/__init__.py
CHANGED
tactus/core/dsl_stubs.py
CHANGED
|
@@ -413,6 +413,17 @@ def create_dsl_stubs(
|
|
|
413
413
|
# Task "name" { ... }
|
|
414
414
|
if isinstance(name_or_config, str):
|
|
415
415
|
task_name = name_or_config
|
|
416
|
+
if config is None:
|
|
417
|
+
|
|
418
|
+
def _curried(task_config=None):
|
|
419
|
+
if task_config is None or not hasattr(task_config, "items"):
|
|
420
|
+
raise TypeError(
|
|
421
|
+
f"Task '{task_name}' requires a configuration table. "
|
|
422
|
+
'Use: Task "name" { ... } or name = Task { ... }.'
|
|
423
|
+
)
|
|
424
|
+
return _task(task_name, task_config)
|
|
425
|
+
|
|
426
|
+
return _curried
|
|
416
427
|
task_config = config or {}
|
|
417
428
|
if hasattr(task_config, "__setitem__"):
|
|
418
429
|
try:
|
tactus/core/runtime.py
CHANGED
|
@@ -2499,6 +2499,7 @@ class TactusRuntime:
|
|
|
2499
2499
|
"""
|
|
2500
2500
|
if not self.task_name and self.registry:
|
|
2501
2501
|
explicit_tasks = getattr(self.registry, "tasks", {}) or {}
|
|
2502
|
+
named_procedures = getattr(self.registry, "named_procedures", {}) or {}
|
|
2502
2503
|
|
|
2503
2504
|
def _flatten_tasks(task_map: dict, prefix: str = "") -> list[str]:
|
|
2504
2505
|
names: list[str] = []
|
|
@@ -2528,10 +2529,12 @@ class TactusRuntime:
|
|
|
2528
2529
|
implicit_tasks.append(f"{task}:{retriever_name}")
|
|
2529
2530
|
|
|
2530
2531
|
if explicit_tasks:
|
|
2531
|
-
if
|
|
2532
|
-
self.task_name = next(iter(explicit_tasks.keys()))
|
|
2533
|
-
elif "run" in explicit_tasks:
|
|
2532
|
+
if "run" in explicit_tasks:
|
|
2534
2533
|
self.task_name = "run"
|
|
2534
|
+
elif "main" in named_procedures:
|
|
2535
|
+
self.task_name = None
|
|
2536
|
+
elif len(explicit_tasks) == 1:
|
|
2537
|
+
self.task_name = next(iter(explicit_tasks.keys()))
|
|
2535
2538
|
else:
|
|
2536
2539
|
from tactus.core.exceptions import TaskSelectionRequired
|
|
2537
2540
|
|
|
@@ -2892,7 +2895,11 @@ class TactusRuntime:
|
|
|
2892
2895
|
r"^\s*[A-Za-z_][A-Za-z0-9_]*\s*=\s*(?:"
|
|
2893
2896
|
r"Agent|Toolset|Tool|Model|Module|Signature|LM|Dependency|Prompt|"
|
|
2894
2897
|
r"Task|TaskFunction|Context|Corpus|Retriever|Compactor|function|"
|
|
2895
|
-
r"[A-Za-z_][A-Za-z0-9_]*Retriever|
|
|
2898
|
+
r"[A-Za-z_][A-Za-z0-9_]*Retriever|"
|
|
2899
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Retriever|"
|
|
2900
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Corpus|"
|
|
2901
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Context|"
|
|
2902
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Compactor"
|
|
2896
2903
|
r")\b"
|
|
2897
2904
|
)
|
|
2898
2905
|
# Match function definitions: function name() or local function name()
|
|
@@ -3167,6 +3174,9 @@ class TactusRuntime:
|
|
|
3167
3174
|
except LuaSandboxError as e:
|
|
3168
3175
|
raise TactusRuntimeError(f"Failed to parse DSL: {e}")
|
|
3169
3176
|
|
|
3177
|
+
lua_globals = sandbox.lua.globals()
|
|
3178
|
+
self._register_assignment_tasks(builder, lua_globals)
|
|
3179
|
+
|
|
3170
3180
|
self._expand_inline_task_children(builder.registry)
|
|
3171
3181
|
|
|
3172
3182
|
# Execute IncludeTasks files to register additional tasks
|
|
@@ -3269,7 +3279,6 @@ class TactusRuntime:
|
|
|
3269
3279
|
# The script mode transformation (in _maybe_transform_script_mode_source)
|
|
3270
3280
|
# is designed to skip files with named function definitions to avoid wrapping
|
|
3271
3281
|
# them incorrectly.
|
|
3272
|
-
lua_globals = sandbox.lua.globals()
|
|
3273
3282
|
if "main" in lua_globals:
|
|
3274
3283
|
main_func = lua_globals["main"]
|
|
3275
3284
|
# Check if it's a function and not already registered
|
|
@@ -3297,6 +3306,49 @@ class TactusRuntime:
|
|
|
3297
3306
|
logger.debug(f"Registry after parsing: lua_tools={list(result.registry.lua_tools.keys())}")
|
|
3298
3307
|
return result.registry
|
|
3299
3308
|
|
|
3309
|
+
def _register_assignment_tasks(self, builder: RegistryBuilder, lua_globals: Any) -> None:
|
|
3310
|
+
if not lua_globals:
|
|
3311
|
+
return
|
|
3312
|
+
|
|
3313
|
+
for key, value in lua_globals.items():
|
|
3314
|
+
if not isinstance(key, str):
|
|
3315
|
+
continue
|
|
3316
|
+
if not hasattr(value, "items"):
|
|
3317
|
+
continue
|
|
3318
|
+
try:
|
|
3319
|
+
marker = value["__tactus_task_config"]
|
|
3320
|
+
except Exception:
|
|
3321
|
+
continue
|
|
3322
|
+
if not marker:
|
|
3323
|
+
continue
|
|
3324
|
+
task_config = lua_table_to_dict(value)
|
|
3325
|
+
if not isinstance(task_config, dict):
|
|
3326
|
+
continue
|
|
3327
|
+
if key in builder.registry.tasks:
|
|
3328
|
+
continue
|
|
3329
|
+
if "entry" in task_config and not callable(task_config["entry"]):
|
|
3330
|
+
raise TactusRuntimeError(f"Task '{key}' entry must be a function")
|
|
3331
|
+
builder.register_task(key, task_config)
|
|
3332
|
+
|
|
3333
|
+
child_sources = task_config.get("__tactus_child_tasks")
|
|
3334
|
+
if isinstance(child_sources, dict):
|
|
3335
|
+
child_iter = child_sources.items()
|
|
3336
|
+
else:
|
|
3337
|
+
child_iter = value.items()
|
|
3338
|
+
for child_key, child_value in child_iter:
|
|
3339
|
+
child_name = child_key if isinstance(child_key, str) else None
|
|
3340
|
+
if not hasattr(child_value, "items"):
|
|
3341
|
+
continue
|
|
3342
|
+
child_config = lua_table_to_dict(child_value)
|
|
3343
|
+
if not child_name and isinstance(child_config, dict):
|
|
3344
|
+
child_name = child_config.get("__task_name")
|
|
3345
|
+
if child_name and isinstance(child_config, dict):
|
|
3346
|
+
if "entry" in child_config and not callable(child_config["entry"]):
|
|
3347
|
+
raise TactusRuntimeError(
|
|
3348
|
+
f"Task '{key}:{child_name}' entry must be a function"
|
|
3349
|
+
)
|
|
3350
|
+
builder.register_task(child_name, child_config, parent=key)
|
|
3351
|
+
|
|
3300
3352
|
def _registry_to_config(self, registry: ProcedureRegistry) -> dict[str, Any]:
|
|
3301
3353
|
"""
|
|
3302
3354
|
Convert registry to config dict format.
|
tactus/plugins/noaa.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NOAA AFD helper tools for Tactus demos.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def fetch_noaa_afd(
|
|
14
|
+
*,
|
|
15
|
+
wfo: str,
|
|
16
|
+
max_items: int = 5,
|
|
17
|
+
output_root: str = "tests/fixtures/noaa_afd",
|
|
18
|
+
corpus_root: str = "tests/fixtures/noaa_afd_corpus",
|
|
19
|
+
force: bool = True,
|
|
20
|
+
) -> Dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Fetch NOAA AFD fixtures and import them into a Biblicus corpus (no index).
|
|
23
|
+
|
|
24
|
+
:param wfo: Weather Forecast Office code (e.g., MFL).
|
|
25
|
+
:type wfo: str
|
|
26
|
+
:param max_items: Maximum number of items to fetch.
|
|
27
|
+
:type max_items: int
|
|
28
|
+
:param output_root: Directory for raw fixture output.
|
|
29
|
+
:type output_root: str
|
|
30
|
+
:param corpus_root: Directory for Biblicus corpus output.
|
|
31
|
+
:type corpus_root: str
|
|
32
|
+
:param force: Whether to recreate the corpus directory.
|
|
33
|
+
:type force: bool
|
|
34
|
+
:return: Summary of the fetch/import operation.
|
|
35
|
+
:rtype: dict[str, Any]
|
|
36
|
+
"""
|
|
37
|
+
repo_root = Path(__file__).resolve().parents[2]
|
|
38
|
+
fetch_script = repo_root / "scripts" / "fetch_noaa_afd_corpus.py"
|
|
39
|
+
prepare_script = repo_root / "scripts" / "prepare_noaa_afd_biblicus_corpus.py"
|
|
40
|
+
|
|
41
|
+
output_root_path = Path(output_root)
|
|
42
|
+
corpus_root_path = Path(corpus_root) / wfo.upper()
|
|
43
|
+
|
|
44
|
+
fetch_args = [
|
|
45
|
+
sys.executable,
|
|
46
|
+
str(fetch_script),
|
|
47
|
+
"--wfo",
|
|
48
|
+
wfo,
|
|
49
|
+
"--max-items",
|
|
50
|
+
str(max_items),
|
|
51
|
+
"--output",
|
|
52
|
+
str(output_root_path),
|
|
53
|
+
]
|
|
54
|
+
subprocess.run(fetch_args, check=True)
|
|
55
|
+
|
|
56
|
+
prepare_args = [
|
|
57
|
+
sys.executable,
|
|
58
|
+
str(prepare_script),
|
|
59
|
+
"--wfo",
|
|
60
|
+
wfo,
|
|
61
|
+
"--corpus",
|
|
62
|
+
str(corpus_root_path),
|
|
63
|
+
"--no-index",
|
|
64
|
+
]
|
|
65
|
+
if force:
|
|
66
|
+
prepare_args.append("--force")
|
|
67
|
+
subprocess.run(prepare_args, check=True)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"status": "ok",
|
|
71
|
+
"wfo": wfo,
|
|
72
|
+
"max_items": max_items,
|
|
73
|
+
"output_root": str(output_root_path),
|
|
74
|
+
"corpus_root": str(corpus_root_path),
|
|
75
|
+
"indexed": False,
|
|
76
|
+
}
|
tactus/stdlib/biblicus/text.py
CHANGED
|
@@ -62,10 +62,18 @@ def _normalize_client_config(client: Any) -> Any:
|
|
|
62
62
|
|
|
63
63
|
payload = dict(client)
|
|
64
64
|
model = payload.get("model")
|
|
65
|
-
provider = payload.get("provider")
|
|
66
65
|
if not model:
|
|
67
66
|
raise ValueError("client.model is required")
|
|
68
67
|
|
|
68
|
+
provider, model = _resolve_provider_and_model(payload.get("provider"), model)
|
|
69
|
+
payload["provider"] = provider
|
|
70
|
+
payload["model"] = model
|
|
71
|
+
|
|
72
|
+
biblicus = _require_biblicus_text()
|
|
73
|
+
return biblicus["LlmClientConfig"](**payload)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _resolve_provider_and_model(provider: str | None, model: Any) -> tuple[str, Any]:
|
|
69
77
|
if provider is None and isinstance(model, str) and "/" in model:
|
|
70
78
|
provider, model = model.split("/", 1)
|
|
71
79
|
elif provider is not None and isinstance(model, str) and model.startswith(f"{provider}/"):
|
|
@@ -74,11 +82,7 @@ def _normalize_client_config(client: Any) -> Any:
|
|
|
74
82
|
if provider is None:
|
|
75
83
|
raise ValueError("client.provider is required when model lacks a provider prefix")
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
payload["model"] = model
|
|
79
|
-
|
|
80
|
-
biblicus = _require_biblicus_text()
|
|
81
|
-
return biblicus["LlmClientConfig"](**payload)
|
|
85
|
+
return provider, model
|
|
82
86
|
|
|
83
87
|
|
|
84
88
|
def _prepare_request(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
@@ -97,11 +101,7 @@ def _prepare_request(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
97
101
|
|
|
98
102
|
|
|
99
103
|
def _maybe_mock(tool_name: str, payload: Dict[str, Any]) -> Dict[str, Any] | None:
|
|
100
|
-
|
|
101
|
-
from tactus.core.mocking import get_current_mock_manager
|
|
102
|
-
except Exception:
|
|
103
|
-
return None
|
|
104
|
-
mock_manager = get_current_mock_manager()
|
|
104
|
+
mock_manager = _get_mock_manager()
|
|
105
105
|
if mock_manager is None:
|
|
106
106
|
return None
|
|
107
107
|
mock_result = mock_manager.get_mock_response(tool_name, payload)
|
|
@@ -111,54 +111,73 @@ def _maybe_mock(tool_name: str, payload: Dict[str, Any]) -> Dict[str, Any] | Non
|
|
|
111
111
|
return mock_result
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def
|
|
114
|
+
def _get_mock_manager() -> Any | None:
|
|
115
|
+
try:
|
|
116
|
+
from tactus.core.mocking import get_current_mock_manager
|
|
117
|
+
except Exception:
|
|
118
|
+
return None
|
|
119
|
+
return get_current_mock_manager()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _run_text_tool(
|
|
123
|
+
request: Dict[str, Any],
|
|
124
|
+
*,
|
|
125
|
+
tool_name: str,
|
|
126
|
+
request_model_key: str,
|
|
127
|
+
apply_key: str,
|
|
128
|
+
) -> Dict[str, Any]:
|
|
115
129
|
payload = _prepare_request(request)
|
|
116
|
-
mock_result = _maybe_mock(
|
|
130
|
+
mock_result = _maybe_mock(tool_name, payload)
|
|
117
131
|
if mock_result is not None:
|
|
118
132
|
return mock_result
|
|
119
133
|
biblicus = _require_biblicus_text()
|
|
120
|
-
result = biblicus[
|
|
134
|
+
result = biblicus[apply_key](biblicus[request_model_key](**payload))
|
|
121
135
|
return result.model_dump()
|
|
122
136
|
|
|
123
137
|
|
|
138
|
+
def extract(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
139
|
+
return _run_text_tool(
|
|
140
|
+
request,
|
|
141
|
+
tool_name="biblicus.text.extract",
|
|
142
|
+
request_model_key="TextExtractRequest",
|
|
143
|
+
apply_key="apply_text_extract",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
124
147
|
def slice(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return result.model_dump()
|
|
148
|
+
return _run_text_tool(
|
|
149
|
+
request,
|
|
150
|
+
tool_name="biblicus.text.slice",
|
|
151
|
+
request_model_key="TextSliceRequest",
|
|
152
|
+
apply_key="apply_text_slice",
|
|
153
|
+
)
|
|
132
154
|
|
|
133
155
|
|
|
134
156
|
def annotate(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return result.model_dump()
|
|
157
|
+
return _run_text_tool(
|
|
158
|
+
request,
|
|
159
|
+
tool_name="biblicus.text.annotate",
|
|
160
|
+
request_model_key="TextAnnotateRequest",
|
|
161
|
+
apply_key="apply_text_annotate",
|
|
162
|
+
)
|
|
142
163
|
|
|
143
164
|
|
|
144
165
|
def redact(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return result.model_dump()
|
|
166
|
+
return _run_text_tool(
|
|
167
|
+
request,
|
|
168
|
+
tool_name="biblicus.text.redact",
|
|
169
|
+
request_model_key="TextRedactRequest",
|
|
170
|
+
apply_key="apply_text_redact",
|
|
171
|
+
)
|
|
152
172
|
|
|
153
173
|
|
|
154
174
|
def link(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return result.model_dump()
|
|
175
|
+
return _run_text_tool(
|
|
176
|
+
request,
|
|
177
|
+
tool_name="biblicus.text.link",
|
|
178
|
+
request_model_key="TextLinkRequest",
|
|
179
|
+
apply_key="apply_text_link",
|
|
180
|
+
)
|
|
162
181
|
|
|
163
182
|
|
|
164
183
|
def strip_span_tags(marked_up_text: str) -> str:
|
|
@@ -131,29 +131,9 @@ class TactusDSLVisitor(LuaParserVisitor):
|
|
|
131
131
|
assignment_target_name = assignment_target_node.NAME().getText()
|
|
132
132
|
self._track_retriever_alias(assignment_target_name, expression_list)
|
|
133
133
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"default_model": self.builder.set_default_model,
|
|
138
|
-
"return_prompt": self.builder.set_return_prompt,
|
|
139
|
-
"error_prompt": self.builder.set_error_prompt,
|
|
140
|
-
"status_prompt": self.builder.set_status_prompt,
|
|
141
|
-
"async": self.builder.set_async,
|
|
142
|
-
"max_depth": self.builder.set_max_depth,
|
|
143
|
-
"max_turns": self.builder.set_max_turns,
|
|
144
|
-
}
|
|
145
|
-
if assignment_target_name in setting_handlers_by_name:
|
|
146
|
-
# Get the value from explist
|
|
147
|
-
if expression_list.exp() and len(expression_list.exp()) > 0:
|
|
148
|
-
first_expression = expression_list.exp()[0]
|
|
149
|
-
literal_value = self._extract_literal_value(first_expression)
|
|
150
|
-
# Process the assignment like a function call
|
|
151
|
-
setting_handlers_by_name[assignment_target_name](literal_value)
|
|
152
|
-
else:
|
|
153
|
-
# Check for assignment-based DSL declarations
|
|
154
|
-
# e.g., greeter = Agent {...}, done = Tool {...}
|
|
155
|
-
if expression_list.exp() and len(expression_list.exp()) > 0:
|
|
156
|
-
first_expression = expression_list.exp()[0]
|
|
134
|
+
if not self._apply_setting_assignment(assignment_target_name, expression_list):
|
|
135
|
+
first_expression = self._extract_first_expression(expression_list)
|
|
136
|
+
if first_expression:
|
|
157
137
|
self._check_assignment_based_declaration(
|
|
158
138
|
assignment_target_name, first_expression
|
|
159
139
|
)
|
|
@@ -219,105 +199,116 @@ class TactusDSLVisitor(LuaParserVisitor):
|
|
|
219
199
|
)
|
|
220
200
|
return
|
|
221
201
|
|
|
222
|
-
if function_name == "Agent" and
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
]
|
|
232
|
-
self.builder.register_agent(
|
|
233
|
-
assignment_target_name,
|
|
234
|
-
declaration_config if declaration_config else {},
|
|
235
|
-
None,
|
|
236
|
-
)
|
|
237
|
-
elif function_name == "Tool":
|
|
238
|
-
# Extract config from Tool {...}
|
|
239
|
-
declaration_config = self._extract_single_table_arg(function_call)
|
|
240
|
-
if (
|
|
241
|
-
declaration_config
|
|
242
|
-
and isinstance(declaration_config, dict)
|
|
243
|
-
and isinstance(declaration_config.get("name"), str)
|
|
244
|
-
and declaration_config.get("name") != assignment_target_name
|
|
245
|
-
):
|
|
246
|
-
self._record_error(
|
|
247
|
-
message=(
|
|
248
|
-
f"Tool name mismatch: '{assignment_target_name} = Tool {{ name = \"{declaration_config.get('name')}\" }}'. "
|
|
249
|
-
f"Remove the 'name' field or set it to '{assignment_target_name}'."
|
|
250
|
-
),
|
|
251
|
-
declaration="Tool",
|
|
252
|
-
)
|
|
253
|
-
self.builder.register_tool(
|
|
254
|
-
assignment_target_name,
|
|
255
|
-
declaration_config if declaration_config else {},
|
|
256
|
-
None,
|
|
257
|
-
)
|
|
258
|
-
elif function_name == "Toolset":
|
|
259
|
-
# Extract config from Toolset {...}
|
|
260
|
-
declaration_config = self._extract_single_table_arg(function_call)
|
|
261
|
-
self.builder.register_toolset(
|
|
262
|
-
assignment_target_name,
|
|
263
|
-
declaration_config if declaration_config else {},
|
|
264
|
-
)
|
|
265
|
-
elif function_name == "Context":
|
|
266
|
-
declaration_config = self._extract_single_table_arg(function_call)
|
|
267
|
-
self.builder.register_context(
|
|
268
|
-
assignment_target_name,
|
|
269
|
-
declaration_config if declaration_config else {},
|
|
270
|
-
)
|
|
271
|
-
elif function_name == "Corpus":
|
|
272
|
-
declaration_config = self._extract_single_table_arg(function_call)
|
|
273
|
-
self.builder.register_corpus(
|
|
274
|
-
assignment_target_name,
|
|
275
|
-
declaration_config if declaration_config else {},
|
|
276
|
-
)
|
|
277
|
-
elif function_name == "Retriever":
|
|
278
|
-
declaration_config = self._extract_single_table_arg(function_call)
|
|
202
|
+
if function_name == "Agent" and is_chained_method_call:
|
|
203
|
+
return
|
|
204
|
+
handled = self._register_assignment_based_declaration(
|
|
205
|
+
assignment_target_name, function_name, function_call
|
|
206
|
+
)
|
|
207
|
+
if handled:
|
|
208
|
+
return
|
|
209
|
+
declaration_config = self._extract_single_table_arg(function_call)
|
|
210
|
+
if isinstance(declaration_config, dict) and "corpus" in declaration_config:
|
|
279
211
|
retriever_id = self._resolve_retriever_id_from_call(function_call)
|
|
280
|
-
if
|
|
281
|
-
isinstance(declaration_config, dict)
|
|
282
|
-
and retriever_id
|
|
283
|
-
and "retriever_id" not in declaration_config
|
|
284
|
-
):
|
|
212
|
+
if retriever_id and "retriever_id" not in declaration_config:
|
|
285
213
|
declaration_config["retriever_id"] = retriever_id
|
|
286
214
|
self.builder.register_retriever(
|
|
287
215
|
assignment_target_name,
|
|
288
|
-
declaration_config
|
|
289
|
-
)
|
|
290
|
-
elif function_name == "Compactor":
|
|
291
|
-
declaration_config = self._extract_single_table_arg(function_call)
|
|
292
|
-
self.builder.register_compactor(
|
|
293
|
-
assignment_target_name,
|
|
294
|
-
declaration_config if declaration_config else {},
|
|
295
|
-
)
|
|
296
|
-
elif function_name == "Procedure":
|
|
297
|
-
# New assignment syntax: main = Procedure { function(input) ... }
|
|
298
|
-
# Register as a named procedure
|
|
299
|
-
self.builder.register_named_procedure(
|
|
300
|
-
assignment_target_name,
|
|
301
|
-
None, # Function not available during validation
|
|
302
|
-
{}, # Input schema will be extracted from top-level input {}
|
|
303
|
-
{}, # Output schema will be extracted from top-level output {}
|
|
304
|
-
{}, # State schema
|
|
216
|
+
declaration_config,
|
|
305
217
|
)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
218
|
+
|
|
219
|
+
def _apply_setting_assignment(self, assignment_target_name: str, expression_list) -> bool:
|
|
220
|
+
setting_handlers_by_name = {
|
|
221
|
+
"default_provider": self.builder.set_default_provider,
|
|
222
|
+
"default_model": self.builder.set_default_model,
|
|
223
|
+
"return_prompt": self.builder.set_return_prompt,
|
|
224
|
+
"error_prompt": self.builder.set_error_prompt,
|
|
225
|
+
"status_prompt": self.builder.set_status_prompt,
|
|
226
|
+
"async": self.builder.set_async,
|
|
227
|
+
"max_depth": self.builder.set_max_depth,
|
|
228
|
+
"max_turns": self.builder.set_max_turns,
|
|
229
|
+
}
|
|
230
|
+
if assignment_target_name not in setting_handlers_by_name:
|
|
231
|
+
return False
|
|
232
|
+
first_expression = self._extract_first_expression(expression_list)
|
|
233
|
+
if not first_expression:
|
|
234
|
+
return True
|
|
235
|
+
literal_value = self._extract_literal_value(first_expression)
|
|
236
|
+
setting_handlers_by_name[assignment_target_name](literal_value)
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
def _extract_first_expression(self, expression_list):
|
|
240
|
+
exp_list = getattr(expression_list, "exp", None)
|
|
241
|
+
if not callable(exp_list):
|
|
242
|
+
return None
|
|
243
|
+
expressions = exp_list()
|
|
244
|
+
if expressions:
|
|
245
|
+
return expressions[0]
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
def _register_assignment_based_declaration(
|
|
249
|
+
self,
|
|
250
|
+
assignment_target_name: str,
|
|
251
|
+
function_name: str,
|
|
252
|
+
function_call,
|
|
253
|
+
) -> bool:
|
|
254
|
+
declaration_config = self._extract_single_table_arg(function_call)
|
|
255
|
+
normalized_config = declaration_config if declaration_config else {}
|
|
256
|
+
if function_name == "Agent":
|
|
257
|
+
if "tools" in normalized_config:
|
|
258
|
+
tool_name_list = normalized_config["tools"]
|
|
259
|
+
if isinstance(tool_name_list, list):
|
|
260
|
+
normalized_config["tools"] = [
|
|
261
|
+
tool_name for tool_name in tool_name_list if tool_name is not None
|
|
262
|
+
]
|
|
263
|
+
self.builder.register_agent(assignment_target_name, normalized_config, None)
|
|
264
|
+
return True
|
|
265
|
+
if function_name == "Tool":
|
|
266
|
+
if (
|
|
267
|
+
isinstance(normalized_config, dict)
|
|
268
|
+
and isinstance(normalized_config.get("name"), str)
|
|
269
|
+
and normalized_config.get("name") != assignment_target_name
|
|
270
|
+
):
|
|
271
|
+
self._record_error(
|
|
272
|
+
message=(
|
|
273
|
+
f"Tool name mismatch: '{assignment_target_name} = Tool {{ name = \"{normalized_config.get('name')}\" }}'. "
|
|
274
|
+
f"Remove the 'name' field or set it to '{assignment_target_name}'."
|
|
275
|
+
),
|
|
276
|
+
declaration="Tool",
|
|
277
|
+
)
|
|
278
|
+
self.builder.register_tool(assignment_target_name, normalized_config, None)
|
|
279
|
+
return True
|
|
280
|
+
if function_name == "Toolset":
|
|
281
|
+
self.builder.register_toolset(assignment_target_name, normalized_config)
|
|
282
|
+
return True
|
|
283
|
+
if function_name == "Context":
|
|
284
|
+
self.builder.register_context(assignment_target_name, normalized_config)
|
|
285
|
+
return True
|
|
286
|
+
if function_name == "Corpus":
|
|
287
|
+
self.builder.register_corpus(assignment_target_name, normalized_config)
|
|
288
|
+
return True
|
|
289
|
+
if function_name == "Retriever":
|
|
290
|
+
retriever_id = self._resolve_retriever_id_from_call(function_call)
|
|
291
|
+
if retriever_id and "retriever_id" not in normalized_config:
|
|
292
|
+
normalized_config["retriever_id"] = retriever_id
|
|
293
|
+
self.builder.register_retriever(assignment_target_name, normalized_config)
|
|
294
|
+
return True
|
|
295
|
+
if function_name == "Compactor":
|
|
296
|
+
self.builder.register_compactor(assignment_target_name, normalized_config)
|
|
297
|
+
return True
|
|
298
|
+
if function_name == "Procedure":
|
|
299
|
+
self.builder.register_named_procedure(
|
|
300
|
+
assignment_target_name,
|
|
301
|
+
None,
|
|
302
|
+
{},
|
|
303
|
+
{},
|
|
304
|
+
{},
|
|
305
|
+
)
|
|
306
|
+
return True
|
|
307
|
+
if function_name == "Task":
|
|
308
|
+
self._processed_task_calls.add(id(function_call))
|
|
309
|
+
self._register_task_declaration(assignment_target_name, function_call)
|
|
310
|
+
return True
|
|
311
|
+
return False
|
|
321
312
|
|
|
322
313
|
def _extract_single_table_arg(self, function_call) -> dict:
|
|
323
314
|
"""Extract a single table argument from a function call like Agent {...}."""
|
|
@@ -1122,23 +1113,23 @@ class TactusDSLVisitor(LuaParserVisitor):
|
|
|
1122
1113
|
return token_text[2:-2]
|
|
1123
1114
|
elif token_text.startswith('"') and token_text.endswith('"'):
|
|
1124
1115
|
# Double-quoted string
|
|
1125
|
-
|
|
1126
|
-
content = content.replace("\\n", "\n")
|
|
1127
|
-
content = content.replace("\\t", "\t")
|
|
1128
|
-
content = content.replace('\\"', '"')
|
|
1129
|
-
content = content.replace("\\\\", "\\")
|
|
1130
|
-
return content
|
|
1116
|
+
return self._unescape_basic_string(token_text[1:-1], '"')
|
|
1131
1117
|
elif token_text.startswith("'") and token_text.endswith("'"):
|
|
1132
1118
|
# Single-quoted string
|
|
1133
|
-
|
|
1134
|
-
content = content.replace("\\n", "\n")
|
|
1135
|
-
content = content.replace("\\t", "\t")
|
|
1136
|
-
content = content.replace("\\'", "'")
|
|
1137
|
-
content = content.replace("\\\\", "\\")
|
|
1138
|
-
return content
|
|
1119
|
+
return self._unescape_basic_string(token_text[1:-1], "'")
|
|
1139
1120
|
|
|
1140
1121
|
return token_text
|
|
1141
1122
|
|
|
1123
|
+
def _unescape_basic_string(self, content: str, quote_char: str) -> str:
|
|
1124
|
+
content = content.replace("\\n", "\n")
|
|
1125
|
+
content = content.replace("\\t", "\t")
|
|
1126
|
+
if quote_char == '"':
|
|
1127
|
+
content = content.replace('\\"', '"')
|
|
1128
|
+
elif quote_char == "'":
|
|
1129
|
+
content = content.replace("\\'", "'")
|
|
1130
|
+
content = content.replace("\\\\", "\\")
|
|
1131
|
+
return content
|
|
1132
|
+
|
|
1142
1133
|
def _parse_table_constructor(self, ctx: LuaParser.TableconstructorContext) -> Any:
|
|
1143
1134
|
"""Parse Lua table constructor to Python dict."""
|
|
1144
1135
|
parsed_table = {}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
tactus/__init__.py,sha256=
|
|
1
|
+
tactus/__init__.py,sha256=FbndoB7McjD-smRU9tGDLvNIw7o7-dCqiKRP3LSD23c,1245
|
|
2
2
|
tactus/adapters/__init__.py,sha256=47Y8kGBR4QGxqEGvjA1mneOSACb2L7oELnj6P2uI7uk,759
|
|
3
3
|
tactus/adapters/broker_log.py,sha256=9ZR-rJdyW6bMNZx3OfXoQEnDxcAzNsiJ8aPxZGqJYrM,6019
|
|
4
4
|
tactus/adapters/cli_hitl.py,sha256=Nrfoi35Ei9fTMReLG2QxKkhKyIvl3pYcAUdQCAUOZDk,17361
|
|
@@ -37,7 +37,7 @@ tactus/core/compaction.py,sha256=mVwFsEb9FEc-bMPFcKgXyAO-pVnAXeQGZka7RWtjVsY,397
|
|
|
37
37
|
tactus/core/config_manager.py,sha256=kxz853j4Nx97SBh8-fAar_OfmfWZvvcfafLyxjTQG1A,35131
|
|
38
38
|
tactus/core/context_assembler.py,sha256=tbd-XACBvAFkBlPrzAAZ_L-2JGnO4meS-GL4ilE7XHw,2406
|
|
39
39
|
tactus/core/context_models.py,sha256=YHKjHKEzLk3fgpkmXYQ9RWDPKRNStt92ve40xcMv3p0,975
|
|
40
|
-
tactus/core/dsl_stubs.py,sha256=
|
|
40
|
+
tactus/core/dsl_stubs.py,sha256=l1icTqTM092Z2AsrdLAo3WjZtDDVggMBG662KdWUIIU,110257
|
|
41
41
|
tactus/core/exceptions.py,sha256=r-4IrZw_WrioBkpMR42Q3LNwEqimJ6jxXgfOo2wANTM,1962
|
|
42
42
|
tactus/core/execution_context.py,sha256=OgTe9E0xc3nTQbCTEaaBfI11dVA1-J0eFz0eQR4XMZY,29402
|
|
43
43
|
tactus/core/lua_sandbox.py,sha256=Ln2P1gdxVl396HLvEw7FmDKV3eVdVdbDzYHMbDSEciY,19106
|
|
@@ -47,7 +47,7 @@ tactus/core/output_validator.py,sha256=LcSjgAiDRvzsj2uWasQihengQRt7R-ZYaPiLQPbZy
|
|
|
47
47
|
tactus/core/registry.py,sha256=z5HFoi1zb81-TJ_6qeTWSte4J0Zw-NYmN19VjjBjJE8,26577
|
|
48
48
|
tactus/core/retrieval.py,sha256=AMDa4X4YWaSDNdf3T3hRUzgM4W1l_sGqxAX5Jki5PH0,12083
|
|
49
49
|
tactus/core/retriever_tasks.py,sha256=i2wsoZN9cZOypkdPAJKqlrRP_jwtqpHVCFoxPL0Iirk,931
|
|
50
|
-
tactus/core/runtime.py,sha256=
|
|
50
|
+
tactus/core/runtime.py,sha256=YMZKfUj2sfyGyBFdKvXWjbt2dbf-mckz4CefeMm_7sw,157790
|
|
51
51
|
tactus/core/template_resolver.py,sha256=r97KzFNaK4nFSoWtIFZeSKyuUWgbp-ay1_BGrb-BgUY,4179
|
|
52
52
|
tactus/core/yaml_parser.py,sha256=JD7Nehaxw3uP1KV_uTU_xiXTbEWqoKOceU5tAJ4lcH8,13985
|
|
53
53
|
tactus/core/dependencies/__init__.py,sha256=28-TM7_i-JqTD3hvkq1kMzr__A8VjfIKXymdW9mn5NM,362
|
|
@@ -76,6 +76,8 @@ tactus/ide/__init__.py,sha256=1fSC0xWP-Lq5wl4FgDq7SMnkvZ0DxXupreTl3ZRX1zw,143
|
|
|
76
76
|
tactus/ide/coding_assistant.py,sha256=i2cfT6uMSM7TEFw-9p9Ed_BWSbAMqgokoaFdbjKAQcg,12187
|
|
77
77
|
tactus/ide/config_server.py,sha256=U8OWxi5l24GH1lUHIAQ8WB8j0cJ5ofLX9iVecW1O2vc,18862
|
|
78
78
|
tactus/ide/server.py,sha256=nE_UDiXJZN7G-RzPD-guZ_4qPxPl722qcrv4UY6bjII,111151
|
|
79
|
+
tactus/plugins/__init__.py,sha256=MOSTLvPnXeDHKNcGzTv26bRxsK62pVRmTjEwbuoaWHk,39
|
|
80
|
+
tactus/plugins/noaa.py,sha256=BTBxmbI55AFgDNB4ZhNR7NaKqJECRvRJRua4gahaSvU,2022
|
|
79
81
|
tactus/primitives/__init__.py,sha256=x6bGwoa9DizKUwqsg7SqURfJxisEdctTCv1XnSAZxIk,1709
|
|
80
82
|
tactus/primitives/control.py,sha256=jw-7ggHtNLfFL5aTUUs6Fo5y4xsxEG8OIRe0RyIjVnc,4783
|
|
81
83
|
tactus/primitives/file.py,sha256=GFHmXOADRllfJw6gHpIdVMmZ_ZS7DVgresQ0F71nqJE,7458
|
|
@@ -122,7 +124,7 @@ tactus/stdlib/README.md,sha256=AqKYj7JxtphfKJlhq_sNezIUPfblzzubzdxE3PheiQE,2576
|
|
|
122
124
|
tactus/stdlib/__init__.py,sha256=NkRsL413VXr0rLAadbb3meP5TelwcrEFVJd1u39XCbk,1047
|
|
123
125
|
tactus/stdlib/loader.py,sha256=qjVnz5mn3Uu7g1O4vjSREHkR-YdRoON1vqJQq-oiFIE,8679
|
|
124
126
|
tactus/stdlib/biblicus/__init__.py,sha256=Y6Nb-wp33KeDtkjBccZrGYlyR98yhZBm1RfmiKIJHm8,50
|
|
125
|
-
tactus/stdlib/biblicus/text.py,sha256=
|
|
127
|
+
tactus/stdlib/biblicus/text.py,sha256=aL7FF5MmX0aXskGwSTD970kGP4C0bAGHm0vJe7MThPU,6255
|
|
126
128
|
tactus/stdlib/classify/__init__.py,sha256=51Lqge0g0Q6GWXkmw42HwuqkkDCsww9VBcoreYId374,5623
|
|
127
129
|
tactus/stdlib/classify/classify.spec.tac,sha256=0yuRD_2dbPKTuhyqwk3vtsj_R3kwGoSEiEF4OY-ARqA,6475
|
|
128
130
|
tactus/stdlib/classify/classify.tac,sha256=KvOXLihspPK1_g2GcT9wnLkynDubglp1S_JUfZlo-88,6850
|
|
@@ -210,7 +212,7 @@ tactus/validation/LuaParserBase.py,sha256=o3klCIY0ANkVCU0VHml0IOYE4CdEledeoyoIAP
|
|
|
210
212
|
tactus/validation/README.md,sha256=AS6vr4blY7IKWRsj4wuvWBHVMTc5fto7IgNmv-Rjkdo,5366
|
|
211
213
|
tactus/validation/__init__.py,sha256=rnap-YvNievWigYYUewuXBcLtAdjZ8YpeJDYS1T7XZM,153
|
|
212
214
|
tactus/validation/error_listener.py,sha256=MPkvsVbojwYjNA8MpapG_GNtR6ZDyb3cTt7aLwekCtM,1010
|
|
213
|
-
tactus/validation/semantic_visitor.py,sha256=
|
|
215
|
+
tactus/validation/semantic_visitor.py,sha256=YLX54qXpGNrJSANr15UfbGmxT017vGE10dBtBZTphIQ,55149
|
|
214
216
|
tactus/validation/validator.py,sha256=JSQHmI3EkNjJsPxrztTszJGuNuHyyaDdRsV62I4P0QU,11911
|
|
215
217
|
tactus/validation/generated/LuaLexer.interp,sha256=B-Xb6HNXS7YYYQB_cvsWzf8OQLFnEhZHDN5vCOyP3yw,20444
|
|
216
218
|
tactus/validation/generated/LuaLexer.py,sha256=6B-HNB_vAp3bA5iACLvMWw0R4KFENsuiG7bccysxbRQ,67252
|
|
@@ -224,8 +226,8 @@ tactus/validation/generated/LuaParserVisitor.py,sha256=ageKSmHPxnO3jBS2fBtkmYBOd
|
|
|
224
226
|
tactus/validation/generated/__init__.py,sha256=5gWlwRI0UvmHw2fnBpj_IG6N8oZeabr5tbj1AODDvjc,196
|
|
225
227
|
tactus/validation/grammar/LuaLexer.g4,sha256=t2MXiTCr127RWAyQGvamkcU_m4veqPzSuHUtAKwalw4,2771
|
|
226
228
|
tactus/validation/grammar/LuaParser.g4,sha256=ceZenb90BdiZmVdOxMGj9qJk3QbbWVZe5HUqPgoePfY,3202
|
|
227
|
-
tactus-0.
|
|
228
|
-
tactus-0.
|
|
229
|
-
tactus-0.
|
|
230
|
-
tactus-0.
|
|
231
|
-
tactus-0.
|
|
229
|
+
tactus-0.39.0.dist-info/METADATA,sha256=5Axn0cmSvHUPhqcpvlbMouG27Gu2YkmxHfINh3ucnig,60383
|
|
230
|
+
tactus-0.39.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
231
|
+
tactus-0.39.0.dist-info/entry_points.txt,sha256=vWseqty8m3z-Worje0IYxlioMjPDCoSsm0AtY4GghBY,47
|
|
232
|
+
tactus-0.39.0.dist-info/licenses/LICENSE,sha256=ivohBcAIYnaLPQ-lKEeCXSMvQUVISpQfKyxHBHoa4GA,1066
|
|
233
|
+
tactus-0.39.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|