tactus 0.38.0__py3-none-any.whl → 0.40.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/cli/app.py +24 -0
- tactus/core/dsl_stubs.py +18 -1
- tactus/core/runtime.py +371 -62
- 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.40.0.dist-info}/METADATA +1 -1
- {tactus-0.38.0.dist-info → tactus-0.40.0.dist-info}/RECORD +13 -11
- {tactus-0.38.0.dist-info → tactus-0.40.0.dist-info}/WHEEL +0 -0
- {tactus-0.38.0.dist-info → tactus-0.40.0.dist-info}/entry_points.txt +0 -0
- {tactus-0.38.0.dist-info → tactus-0.40.0.dist-info}/licenses/LICENSE +0 -0
tactus/__init__.py
CHANGED
tactus/cli/app.py
CHANGED
|
@@ -505,6 +505,12 @@ def run(
|
|
|
505
505
|
real: Optional[list[str]] = typer.Option(
|
|
506
506
|
None, "--real", help="Use real implementation for specific tool(s)"
|
|
507
507
|
),
|
|
508
|
+
auto_deps: bool = typer.Option(
|
|
509
|
+
False, "--auto-deps", help="Automatically run dependency tasks without prompting"
|
|
510
|
+
),
|
|
511
|
+
no_deps: bool = typer.Option(
|
|
512
|
+
False, "--no-deps", help="Fail fast if dependency tasks are required"
|
|
513
|
+
),
|
|
508
514
|
sandbox: Optional[bool] = typer.Option(
|
|
509
515
|
None,
|
|
510
516
|
"--sandbox/--no-sandbox",
|
|
@@ -568,6 +574,15 @@ def run(
|
|
|
568
574
|
console.print(f"[red]Error:[/red] Workflow file not found: {workflow_file}")
|
|
569
575
|
raise typer.Exit(1)
|
|
570
576
|
|
|
577
|
+
if not isinstance(auto_deps, bool):
|
|
578
|
+
auto_deps = False
|
|
579
|
+
if not isinstance(no_deps, bool):
|
|
580
|
+
no_deps = False
|
|
581
|
+
|
|
582
|
+
if auto_deps and no_deps:
|
|
583
|
+
console.print("[red]Error:[/red] --auto-deps and --no-deps cannot be combined")
|
|
584
|
+
raise typer.Exit(1)
|
|
585
|
+
|
|
571
586
|
# Determine format based on extension
|
|
572
587
|
file_format = "lua" if workflow_file.suffix in [".tac", ".lua"] else "yaml"
|
|
573
588
|
|
|
@@ -808,6 +823,15 @@ def run(
|
|
|
808
823
|
tool_paths=tool_paths,
|
|
809
824
|
source_file_path=str(workflow_file),
|
|
810
825
|
)
|
|
826
|
+
runtime.dependency_mode = "auto" if auto_deps else "none" if no_deps else "prompt"
|
|
827
|
+
|
|
828
|
+
def _dependency_prompt_handler(plan, label: str) -> bool:
|
|
829
|
+
pending = [task.kind for task in plan.tasks if task.status != "complete"]
|
|
830
|
+
pending_summary = ", ".join(pending) if pending else "none"
|
|
831
|
+
console.print(f"[yellow]Dependencies required for {label}: {pending_summary}[/yellow]")
|
|
832
|
+
return typer.confirm("Run dependencies now?", default=False)
|
|
833
|
+
|
|
834
|
+
runtime.dependency_prompt_handler = _dependency_prompt_handler
|
|
811
835
|
|
|
812
836
|
# Always create a mock manager so Mocks {} blocks can register tool mocks.
|
|
813
837
|
from tactus.core.mocking import MockManager, set_current_mock_manager
|
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:
|
|
@@ -450,7 +461,13 @@ def create_dsl_stubs(
|
|
|
450
461
|
if hasattr(task_config, "items"):
|
|
451
462
|
child_tasks = {}
|
|
452
463
|
for key, value in task_config.items():
|
|
453
|
-
if isinstance(key, str) and hasattr(value, "items"):
|
|
464
|
+
if not (isinstance(key, str) and hasattr(value, "items")):
|
|
465
|
+
continue
|
|
466
|
+
try:
|
|
467
|
+
marker = value["__tactus_task_config"]
|
|
468
|
+
except Exception:
|
|
469
|
+
marker = False
|
|
470
|
+
if marker:
|
|
454
471
|
child_tasks[key] = value
|
|
455
472
|
if child_tasks:
|
|
456
473
|
try:
|
tactus/core/runtime.py
CHANGED
|
@@ -14,7 +14,7 @@ import logging
|
|
|
14
14
|
import time
|
|
15
15
|
import uuid
|
|
16
16
|
from pathlib import Path
|
|
17
|
-
from typing import Any, Dict, List, Optional
|
|
17
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
18
18
|
|
|
19
19
|
from tactus.core.registry import ProcedureRegistry, RegistryBuilder, TaskDeclaration
|
|
20
20
|
from tactus.core.dsl_stubs import create_dsl_stubs, lua_table_to_dict
|
|
@@ -28,6 +28,9 @@ from tactus.protocols.storage import StorageBackend
|
|
|
28
28
|
from tactus.protocols.hitl import HITLHandler
|
|
29
29
|
from tactus.protocols.chat_recorder import ChatRecorder
|
|
30
30
|
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from biblicus.corpus import Corpus
|
|
33
|
+
|
|
31
34
|
# For backwards compatibility with YAML
|
|
32
35
|
try:
|
|
33
36
|
from tactus.core.yaml_parser import ProcedureYAMLParser, ProcedureConfigError
|
|
@@ -142,6 +145,8 @@ class TactusRuntime:
|
|
|
142
145
|
self.tool_paths = tool_paths or []
|
|
143
146
|
self.recursion_depth = recursion_depth
|
|
144
147
|
self.external_config = external_config or {}
|
|
148
|
+
self.dependency_mode = self.external_config.get("dependency_mode", "prompt")
|
|
149
|
+
self.dependency_prompt_handler = None
|
|
145
150
|
self.run_id = run_id
|
|
146
151
|
self.source_file_path = source_file_path
|
|
147
152
|
|
|
@@ -2499,6 +2504,7 @@ class TactusRuntime:
|
|
|
2499
2504
|
"""
|
|
2500
2505
|
if not self.task_name and self.registry:
|
|
2501
2506
|
explicit_tasks = getattr(self.registry, "tasks", {}) or {}
|
|
2507
|
+
named_procedures = getattr(self.registry, "named_procedures", {}) or {}
|
|
2502
2508
|
|
|
2503
2509
|
def _flatten_tasks(task_map: dict, prefix: str = "") -> list[str]:
|
|
2504
2510
|
names: list[str] = []
|
|
@@ -2528,10 +2534,12 @@ class TactusRuntime:
|
|
|
2528
2534
|
implicit_tasks.append(f"{task}:{retriever_name}")
|
|
2529
2535
|
|
|
2530
2536
|
if explicit_tasks:
|
|
2531
|
-
if
|
|
2532
|
-
self.task_name = next(iter(explicit_tasks.keys()))
|
|
2533
|
-
elif "run" in explicit_tasks:
|
|
2537
|
+
if "run" in explicit_tasks:
|
|
2534
2538
|
self.task_name = "run"
|
|
2539
|
+
elif "main" in named_procedures:
|
|
2540
|
+
self.task_name = None
|
|
2541
|
+
elif len(explicit_tasks) == 1:
|
|
2542
|
+
self.task_name = next(iter(explicit_tasks.keys()))
|
|
2535
2543
|
else:
|
|
2536
2544
|
from tactus.core.exceptions import TaskSelectionRequired
|
|
2537
2545
|
|
|
@@ -2549,6 +2557,7 @@ class TactusRuntime:
|
|
|
2549
2557
|
return self._execute_task(self.task_name)
|
|
2550
2558
|
|
|
2551
2559
|
if self.registry:
|
|
2560
|
+
self._execute_run_dependencies()
|
|
2552
2561
|
# Check for named 'main' procedure first
|
|
2553
2562
|
if "main" in self.registry.named_procedures:
|
|
2554
2563
|
logger.info("Executing named 'main' procedure")
|
|
@@ -2636,10 +2645,169 @@ class TactusRuntime:
|
|
|
2636
2645
|
logger.error(f"Legacy procedure execution failed: {e}")
|
|
2637
2646
|
raise
|
|
2638
2647
|
|
|
2639
|
-
def
|
|
2648
|
+
def _parse_task_dependencies(self, task_payload: dict[str, Any]) -> list[str]:
|
|
2649
|
+
depends_on = task_payload.get("depends_on")
|
|
2650
|
+
if isinstance(depends_on, str):
|
|
2651
|
+
return [depends_on]
|
|
2652
|
+
if isinstance(depends_on, list):
|
|
2653
|
+
return [item for item in depends_on if isinstance(item, str) and item.strip()]
|
|
2654
|
+
return []
|
|
2655
|
+
|
|
2656
|
+
def _parse_task_provides(self, task_payload: dict[str, Any]) -> list[dict[str, Any]]:
|
|
2657
|
+
provides = task_payload.get("provides")
|
|
2658
|
+
if isinstance(provides, dict):
|
|
2659
|
+
return [provides]
|
|
2660
|
+
if isinstance(provides, list):
|
|
2661
|
+
return [item for item in provides if isinstance(item, dict)]
|
|
2662
|
+
return []
|
|
2663
|
+
|
|
2664
|
+
def _iter_task_declarations(self) -> list[tuple[str, TaskDeclaration]]:
|
|
2665
|
+
if not self.registry:
|
|
2666
|
+
return []
|
|
2667
|
+
tasks = getattr(self.registry, "tasks", None) or {}
|
|
2668
|
+
if not tasks:
|
|
2669
|
+
return []
|
|
2670
|
+
|
|
2671
|
+
def walk(task: TaskDeclaration, prefix: str) -> list[tuple[str, TaskDeclaration]]:
|
|
2672
|
+
full_name = f"{prefix}:{task.name}" if prefix else task.name
|
|
2673
|
+
pairs = [(full_name, task)]
|
|
2674
|
+
for child in task.children.values():
|
|
2675
|
+
pairs.extend(walk(child, full_name))
|
|
2676
|
+
return pairs
|
|
2677
|
+
|
|
2678
|
+
pairs: list[tuple[str, TaskDeclaration]] = []
|
|
2679
|
+
for task in tasks.values():
|
|
2680
|
+
pairs.extend(walk(task, ""))
|
|
2681
|
+
return pairs
|
|
2682
|
+
|
|
2683
|
+
def _find_task_providing(self, *, kind: str, corpus_name: Optional[str]) -> Optional[str]:
|
|
2684
|
+
if not self.registry:
|
|
2685
|
+
return None
|
|
2686
|
+
matches: list[str] = []
|
|
2687
|
+
normalize_task_kind = self._normalize_task_kind
|
|
2688
|
+
|
|
2689
|
+
for full_name, task in self._iter_task_declarations():
|
|
2690
|
+
payload = task.model_dump()
|
|
2691
|
+
for provides in self._parse_task_provides(payload):
|
|
2692
|
+
provided_kind = provides.get("kind")
|
|
2693
|
+
provided_corpus = provides.get("corpus")
|
|
2694
|
+
if not isinstance(provided_kind, str):
|
|
2695
|
+
continue
|
|
2696
|
+
if normalize_task_kind(provided_kind) != kind:
|
|
2697
|
+
continue
|
|
2698
|
+
if provided_corpus is None or provided_corpus == corpus_name:
|
|
2699
|
+
matches.append(full_name)
|
|
2700
|
+
if matches:
|
|
2701
|
+
return matches[0]
|
|
2702
|
+
if kind == "load" and corpus_name and len(self.registry.corpora) == 1:
|
|
2703
|
+
tasks = getattr(self.registry, "tasks", None) or {}
|
|
2704
|
+
if "load" in tasks:
|
|
2705
|
+
return "load"
|
|
2706
|
+
return None
|
|
2707
|
+
|
|
2708
|
+
@staticmethod
|
|
2709
|
+
def _normalize_task_kind(value: str) -> str:
|
|
2710
|
+
if not isinstance(value, str):
|
|
2711
|
+
return ""
|
|
2712
|
+
cleaned = value.strip().lower()
|
|
2713
|
+
aliases = {
|
|
2714
|
+
"fetch": "load",
|
|
2715
|
+
"sync": "load",
|
|
2716
|
+
"build": "index",
|
|
2717
|
+
"run": "query",
|
|
2718
|
+
}
|
|
2719
|
+
return aliases.get(cleaned, cleaned)
|
|
2720
|
+
|
|
2721
|
+
def _open_or_init_corpus(self, corpus_root: Path) -> "Corpus":
|
|
2722
|
+
from biblicus.corpus import Corpus
|
|
2723
|
+
|
|
2724
|
+
corpus = Corpus(corpus_root.resolve())
|
|
2725
|
+
try:
|
|
2726
|
+
corpus.load_catalog()
|
|
2727
|
+
except FileNotFoundError:
|
|
2728
|
+
corpus = Corpus.init(corpus_root, force=False)
|
|
2729
|
+
return corpus
|
|
2730
|
+
|
|
2731
|
+
def _corpus_pipeline_config(self, corpus_decl: Any) -> Optional[dict]:
|
|
2732
|
+
config = corpus_decl.config if isinstance(corpus_decl.config, dict) else {}
|
|
2733
|
+
corpus_configuration = config.get("configuration", {}) if isinstance(config, dict) else {}
|
|
2734
|
+
pipeline = (
|
|
2735
|
+
corpus_configuration.get("pipeline", {})
|
|
2736
|
+
if isinstance(corpus_configuration, dict)
|
|
2737
|
+
else {}
|
|
2738
|
+
)
|
|
2739
|
+
if not isinstance(pipeline, dict):
|
|
2740
|
+
return None
|
|
2741
|
+
if "extract" in pipeline and isinstance(pipeline.get("extract"), dict):
|
|
2742
|
+
return pipeline.get("extract")
|
|
2743
|
+
if "steps" in pipeline:
|
|
2744
|
+
return pipeline
|
|
2745
|
+
return None
|
|
2746
|
+
|
|
2747
|
+
def _retriever_index_config(self, retriever_decl: Any) -> dict:
|
|
2748
|
+
config = retriever_decl.config if isinstance(retriever_decl.config, dict) else {}
|
|
2749
|
+
configuration = config.get("configuration", {}) if isinstance(config, dict) else {}
|
|
2750
|
+
pipeline = configuration.get("pipeline", {}) if isinstance(configuration, dict) else {}
|
|
2751
|
+
if not pipeline and isinstance(config.get("pipeline"), dict):
|
|
2752
|
+
pipeline = config.get("pipeline") or {}
|
|
2753
|
+
index_config = pipeline.get("index", {}) if isinstance(pipeline, dict) else {}
|
|
2754
|
+
if isinstance(index_config, dict):
|
|
2755
|
+
return index_config
|
|
2756
|
+
return {}
|
|
2757
|
+
|
|
2758
|
+
def _prompt_dependency_plan(self, *, plan: Any, label: str) -> bool:
|
|
2759
|
+
prompt_handler = getattr(self, "dependency_prompt_handler", None)
|
|
2760
|
+
if callable(prompt_handler):
|
|
2761
|
+
return bool(prompt_handler(plan, label))
|
|
2762
|
+
pending = [task.kind for task in plan.tasks if task.status != "complete"]
|
|
2763
|
+
prompt = f"Run dependencies for {label}? ({', '.join(pending)}) [y/N]: "
|
|
2764
|
+
response = input(prompt).strip().lower()
|
|
2765
|
+
return response in {"y", "yes"}
|
|
2766
|
+
|
|
2767
|
+
def _execute_dependency_plan(
|
|
2768
|
+
self,
|
|
2769
|
+
*,
|
|
2770
|
+
plan: Any,
|
|
2771
|
+
corpus: Any,
|
|
2772
|
+
label: str,
|
|
2773
|
+
load_task_name: Optional[str] = None,
|
|
2774
|
+
extract_task_name: Optional[str] = None,
|
|
2775
|
+
index_task_name: Optional[str] = None,
|
|
2776
|
+
) -> list[Any]:
|
|
2777
|
+
mode = self.dependency_mode
|
|
2778
|
+
if plan.status == "complete":
|
|
2779
|
+
return []
|
|
2780
|
+
if plan.status == "blocked":
|
|
2781
|
+
raise RuntimeError(plan.root.reason or f"Dependencies blocked for {label}")
|
|
2782
|
+
if mode == "none":
|
|
2783
|
+
raise RuntimeError(f"Dependencies missing for {label}")
|
|
2784
|
+
if mode not in {"prompt", "auto"}:
|
|
2785
|
+
raise RuntimeError(f"Unsupported dependency mode: {mode}")
|
|
2786
|
+
if mode == "prompt" and not self._prompt_dependency_plan(plan=plan, label=label):
|
|
2787
|
+
raise RuntimeError(f"Dependencies declined for {label}")
|
|
2788
|
+
|
|
2789
|
+
from biblicus.workflow import build_default_handler_registry
|
|
2790
|
+
|
|
2791
|
+
handler_registry = build_default_handler_registry(corpus)
|
|
2792
|
+
if load_task_name:
|
|
2793
|
+
handler_registry["load"] = lambda _: self._execute_task(load_task_name)
|
|
2794
|
+
if extract_task_name:
|
|
2795
|
+
handler_registry["extract"] = lambda _: self._execute_task(extract_task_name)
|
|
2796
|
+
if index_task_name:
|
|
2797
|
+
handler_registry["index"] = lambda _: self._execute_task(index_task_name)
|
|
2798
|
+
return plan.execute(mode="auto", handler_registry=handler_registry)
|
|
2799
|
+
|
|
2800
|
+
def _execute_task(self, task_name: str, *, _stack: Optional[list[str]] = None) -> Any:
|
|
2640
2801
|
if not self.registry:
|
|
2641
2802
|
raise RuntimeError("No registry available for task execution")
|
|
2642
2803
|
|
|
2804
|
+
stack = list(_stack or [])
|
|
2805
|
+
if task_name in stack:
|
|
2806
|
+
raise RuntimeError(
|
|
2807
|
+
f"Task dependency cycle detected: {' -> '.join(stack + [task_name])}"
|
|
2808
|
+
)
|
|
2809
|
+
stack.append(task_name)
|
|
2810
|
+
|
|
2643
2811
|
task = self._resolve_task(task_name)
|
|
2644
2812
|
if task is None:
|
|
2645
2813
|
# Allow run fallback to main procedure
|
|
@@ -2647,6 +2815,10 @@ class TactusRuntime:
|
|
|
2647
2815
|
self.task_name = None
|
|
2648
2816
|
return self._execute_workflow()
|
|
2649
2817
|
|
|
2818
|
+
corpus_tasks = self._resolve_corpus_task_targets(task_name)
|
|
2819
|
+
if corpus_tasks:
|
|
2820
|
+
return self._execute_corpus_tasks(task_name, corpus_tasks)
|
|
2821
|
+
|
|
2650
2822
|
retriever_tasks = self._resolve_retriever_task_targets(task_name)
|
|
2651
2823
|
if retriever_tasks:
|
|
2652
2824
|
return self._execute_retriever_tasks(task_name, retriever_tasks)
|
|
@@ -2654,6 +2826,10 @@ class TactusRuntime:
|
|
|
2654
2826
|
raise RuntimeError(f"Task '{task_name}' not found")
|
|
2655
2827
|
|
|
2656
2828
|
task_payload = task.model_dump()
|
|
2829
|
+
if task_name == "run":
|
|
2830
|
+
self._execute_run_dependencies()
|
|
2831
|
+
for dependency in self._parse_task_dependencies(task_payload):
|
|
2832
|
+
self._execute_task(dependency, _stack=stack)
|
|
2657
2833
|
entry = task_payload.get("entry")
|
|
2658
2834
|
if entry is None:
|
|
2659
2835
|
if task.children:
|
|
@@ -2667,20 +2843,74 @@ class TactusRuntime:
|
|
|
2667
2843
|
raise RuntimeError(f"Task '{task_name}' entry must be a function")
|
|
2668
2844
|
return entry()
|
|
2669
2845
|
|
|
2670
|
-
def
|
|
2671
|
-
if not
|
|
2672
|
-
|
|
2846
|
+
def _execute_run_dependencies(self) -> None:
|
|
2847
|
+
if not self.registry:
|
|
2848
|
+
return
|
|
2849
|
+
retrievers = getattr(self.registry, "retrievers", None) or {}
|
|
2850
|
+
if not retrievers:
|
|
2851
|
+
return
|
|
2852
|
+
for retriever_name in retrievers:
|
|
2853
|
+
self._ensure_retriever_dependencies(retriever_name)
|
|
2673
2854
|
|
|
2855
|
+
def _execute_corpus_tasks(self, task_name: str, corpus_names: list[str]) -> Any:
|
|
2856
|
+
if not corpus_names:
|
|
2857
|
+
raise RuntimeError(f"No corpora available for task '{task_name}'")
|
|
2674
2858
|
base_task = task_name.split(":")[0]
|
|
2675
|
-
if base_task == "
|
|
2859
|
+
if base_task == "extract":
|
|
2676
2860
|
results = []
|
|
2677
|
-
for
|
|
2678
|
-
results.append(self.
|
|
2861
|
+
for corpus_name in corpus_names:
|
|
2862
|
+
results.append(self._execute_corpus_extract(corpus_name))
|
|
2679
2863
|
return results[0] if len(results) == 1 else results
|
|
2864
|
+
raise RuntimeError(f"Corpus task '{base_task}' is not supported")
|
|
2680
2865
|
|
|
2681
|
-
|
|
2866
|
+
def _execute_corpus_extract(self, corpus_name: str) -> Any:
|
|
2867
|
+
if not self.registry:
|
|
2868
|
+
raise RuntimeError("No registry available for corpus execution")
|
|
2869
|
+
corpus_decl = self.registry.corpora.get(corpus_name)
|
|
2870
|
+
if corpus_decl is None:
|
|
2871
|
+
raise RuntimeError(f"Corpus '{corpus_name}' not found")
|
|
2872
|
+
corpus_root = corpus_decl.config.get("corpus_root") or corpus_decl.config.get("root")
|
|
2873
|
+
if not corpus_root:
|
|
2874
|
+
raise RuntimeError(f"Corpus '{corpus_name}' is missing a root path")
|
|
2682
2875
|
|
|
2683
|
-
|
|
2876
|
+
pipeline_config = self._corpus_pipeline_config(corpus_decl)
|
|
2877
|
+
load_task_name = self._find_task_providing(kind="load", corpus_name=corpus_name)
|
|
2878
|
+
extract_task_name = self._find_task_providing(kind="extract", corpus_name=corpus_name)
|
|
2879
|
+
|
|
2880
|
+
try:
|
|
2881
|
+
from biblicus.workflow import build_plan_for_extract
|
|
2882
|
+
except Exception as exc:
|
|
2883
|
+
raise RuntimeError(f"Biblicus workflow unavailable: {exc}") from exc
|
|
2884
|
+
|
|
2885
|
+
corpus = self._open_or_init_corpus(Path(corpus_root))
|
|
2886
|
+
plan = build_plan_for_extract(
|
|
2887
|
+
corpus,
|
|
2888
|
+
pipeline_config=pipeline_config,
|
|
2889
|
+
load_handler_available=bool(load_task_name),
|
|
2890
|
+
)
|
|
2891
|
+
results = self._execute_dependency_plan(
|
|
2892
|
+
plan=plan,
|
|
2893
|
+
corpus=corpus,
|
|
2894
|
+
label=f"corpus '{corpus_name}'",
|
|
2895
|
+
load_task_name=load_task_name,
|
|
2896
|
+
extract_task_name=extract_task_name,
|
|
2897
|
+
)
|
|
2898
|
+
return results[-1] if results else {"status": "up-to-date"}
|
|
2899
|
+
|
|
2900
|
+
def _ensure_retriever_dependencies(self, retriever_name: str) -> None:
|
|
2901
|
+
plan, corpus, handlers = self._build_retriever_index_plan(retriever_name)
|
|
2902
|
+
self._execute_dependency_plan(
|
|
2903
|
+
plan=plan,
|
|
2904
|
+
corpus=corpus,
|
|
2905
|
+
label=f"retriever '{retriever_name}'",
|
|
2906
|
+
load_task_name=handlers.get("load"),
|
|
2907
|
+
extract_task_name=handlers.get("extract"),
|
|
2908
|
+
index_task_name=handlers.get("index"),
|
|
2909
|
+
)
|
|
2910
|
+
|
|
2911
|
+
def _build_retriever_index_plan(
|
|
2912
|
+
self, retriever_name: str
|
|
2913
|
+
) -> tuple[Any, Any, dict[str, Optional[str]]]:
|
|
2684
2914
|
if not self.registry:
|
|
2685
2915
|
raise RuntimeError("No registry available for retriever execution")
|
|
2686
2916
|
|
|
@@ -2699,63 +2929,78 @@ class TactusRuntime:
|
|
|
2699
2929
|
if not corpus_root:
|
|
2700
2930
|
raise RuntimeError(f"Corpus '{retriever.corpus}' is missing a root path")
|
|
2701
2931
|
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
if isinstance(
|
|
2706
|
-
else {}
|
|
2707
|
-
)
|
|
2708
|
-
if isinstance(corpus_configuration, dict):
|
|
2709
|
-
pipeline = corpus_configuration.get("pipeline", {}) or {}
|
|
2710
|
-
if isinstance(pipeline, dict):
|
|
2711
|
-
extraction_pipeline = pipeline.get("extract", {}) or {}
|
|
2712
|
-
if isinstance(extraction_pipeline, list):
|
|
2713
|
-
extraction_pipeline = {}
|
|
2714
|
-
|
|
2715
|
-
retriever_id = retriever.config.get("retriever_id") or retriever.config.get(
|
|
2716
|
-
"retriever_type"
|
|
2932
|
+
from tactus.core.retriever_tasks import resolve_retriever_id
|
|
2933
|
+
|
|
2934
|
+
retriever_id = resolve_retriever_id(
|
|
2935
|
+
retriever.config if isinstance(retriever.config, dict) else {}
|
|
2717
2936
|
)
|
|
2718
2937
|
if not retriever_id:
|
|
2719
2938
|
raise RuntimeError(
|
|
2720
|
-
f"Retriever '{retriever_name}' is missing retriever_id; cannot build
|
|
2939
|
+
f"Retriever '{retriever_name}' is missing retriever_id; cannot build plan"
|
|
2721
2940
|
)
|
|
2722
2941
|
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2942
|
+
pipeline_config = self._corpus_pipeline_config(corpus_decl)
|
|
2943
|
+
index_config = self._retriever_index_config(retriever)
|
|
2944
|
+
|
|
2945
|
+
load_task_name = self._find_task_providing(kind="load", corpus_name=retriever.corpus)
|
|
2946
|
+
extract_task_name = self._find_task_providing(kind="extract", corpus_name=retriever.corpus)
|
|
2947
|
+
index_task_name = None
|
|
2948
|
+
for task_name, task in self._iter_task_declarations():
|
|
2949
|
+
for provides in self._parse_task_provides(task.model_dump()):
|
|
2950
|
+
if provides.get("kind") != "index":
|
|
2951
|
+
continue
|
|
2952
|
+
provided_retriever = provides.get("retriever")
|
|
2953
|
+
if provided_retriever in {retriever_name, retriever_id}:
|
|
2954
|
+
index_task_name = task_name
|
|
2955
|
+
break
|
|
2956
|
+
if index_task_name:
|
|
2957
|
+
break
|
|
2730
2958
|
|
|
2731
2959
|
try:
|
|
2732
|
-
from biblicus.
|
|
2733
|
-
from biblicus.extraction import build_extraction_snapshot
|
|
2734
|
-
from biblicus.retrievers import get_retriever
|
|
2960
|
+
from biblicus.workflow import build_plan_for_index
|
|
2735
2961
|
except Exception as exc:
|
|
2736
|
-
raise RuntimeError(f"Biblicus
|
|
2737
|
-
|
|
2738
|
-
corpus =
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
extractor_id="pipeline",
|
|
2746
|
-
configuration_name=f"{retriever.corpus or retriever_name}-extract",
|
|
2747
|
-
configuration=extraction_pipeline,
|
|
2748
|
-
)
|
|
2749
|
-
if isinstance(index_config, dict) and "extraction_snapshot" not in index_config:
|
|
2750
|
-
index_config["extraction_snapshot"] = (
|
|
2751
|
-
f"{extraction_manifest.configuration.extractor_id}:"
|
|
2752
|
-
f"{extraction_manifest.snapshot_id}"
|
|
2753
|
-
)
|
|
2754
|
-
snapshot = retriever_impl.build_snapshot(
|
|
2755
|
-
corpus, configuration_name=configuration_name, configuration=index_config
|
|
2962
|
+
raise RuntimeError(f"Biblicus workflow unavailable: {exc}") from exc
|
|
2963
|
+
|
|
2964
|
+
corpus = self._open_or_init_corpus(Path(corpus_root))
|
|
2965
|
+
plan = build_plan_for_index(
|
|
2966
|
+
corpus,
|
|
2967
|
+
retriever_id,
|
|
2968
|
+
pipeline_config=pipeline_config,
|
|
2969
|
+
index_config=index_config,
|
|
2970
|
+
load_handler_available=bool(load_task_name),
|
|
2756
2971
|
)
|
|
2757
2972
|
|
|
2758
|
-
|
|
2973
|
+
handlers = {
|
|
2974
|
+
"load": load_task_name,
|
|
2975
|
+
"extract": extract_task_name,
|
|
2976
|
+
"index": index_task_name,
|
|
2977
|
+
}
|
|
2978
|
+
return plan, corpus, handlers
|
|
2979
|
+
|
|
2980
|
+
def _execute_retriever_tasks(self, task_name: str, retriever_names: list[str]) -> Any:
|
|
2981
|
+
if not retriever_names:
|
|
2982
|
+
raise RuntimeError(f"No retrievers available for task '{task_name}'")
|
|
2983
|
+
|
|
2984
|
+
base_task = task_name.split(":")[0]
|
|
2985
|
+
if base_task == "index":
|
|
2986
|
+
results = []
|
|
2987
|
+
for retriever_name in retriever_names:
|
|
2988
|
+
results.append(self._execute_retriever_index(retriever_name))
|
|
2989
|
+
return results[0] if len(results) == 1 else results
|
|
2990
|
+
|
|
2991
|
+
raise RuntimeError(f"Retriever task '{base_task}' is not supported")
|
|
2992
|
+
|
|
2993
|
+
def _execute_retriever_index(self, retriever_name: str) -> Any:
|
|
2994
|
+
plan, corpus, handlers = self._build_retriever_index_plan(retriever_name)
|
|
2995
|
+
results = self._execute_dependency_plan(
|
|
2996
|
+
plan=plan,
|
|
2997
|
+
corpus=corpus,
|
|
2998
|
+
label=f"retriever '{retriever_name}'",
|
|
2999
|
+
load_task_name=handlers.get("load"),
|
|
3000
|
+
extract_task_name=handlers.get("extract"),
|
|
3001
|
+
index_task_name=handlers.get("index"),
|
|
3002
|
+
)
|
|
3003
|
+
return results[-1] if results else {"status": "up-to-date"}
|
|
2759
3004
|
|
|
2760
3005
|
def _resolve_task(self, task_name: str) -> Optional[TaskDeclaration]:
|
|
2761
3006
|
if not self.registry:
|
|
@@ -2803,6 +3048,21 @@ class TactusRuntime:
|
|
|
2803
3048
|
return [name for name in targets if name == target]
|
|
2804
3049
|
return targets
|
|
2805
3050
|
|
|
3051
|
+
def _resolve_corpus_task_targets(self, task_name: str) -> list[str]:
|
|
3052
|
+
if not self.registry or not self.registry.corpora:
|
|
3053
|
+
return []
|
|
3054
|
+
segments = [segment for segment in task_name.split(":") if segment]
|
|
3055
|
+
if not segments:
|
|
3056
|
+
return []
|
|
3057
|
+
task = segments[0]
|
|
3058
|
+
target = segments[1] if len(segments) > 1 else None
|
|
3059
|
+
if task != "extract":
|
|
3060
|
+
return []
|
|
3061
|
+
targets = list(self.registry.corpora.keys())
|
|
3062
|
+
if target:
|
|
3063
|
+
return [name for name in targets if name == target]
|
|
3064
|
+
return targets
|
|
3065
|
+
|
|
2806
3066
|
def _expand_inline_task_children(self, registry: ProcedureRegistry) -> None:
|
|
2807
3067
|
if not registry or not registry.tasks:
|
|
2808
3068
|
return
|
|
@@ -2892,7 +3152,11 @@ class TactusRuntime:
|
|
|
2892
3152
|
r"^\s*[A-Za-z_][A-Za-z0-9_]*\s*=\s*(?:"
|
|
2893
3153
|
r"Agent|Toolset|Tool|Model|Module|Signature|LM|Dependency|Prompt|"
|
|
2894
3154
|
r"Task|TaskFunction|Context|Corpus|Retriever|Compactor|function|"
|
|
2895
|
-
r"[A-Za-z_][A-Za-z0-9_]*Retriever|
|
|
3155
|
+
r"[A-Za-z_][A-Za-z0-9_]*Retriever|"
|
|
3156
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Retriever|"
|
|
3157
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Corpus|"
|
|
3158
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Context|"
|
|
3159
|
+
r"[A-Za-z_][A-Za-z0-9_]*\.Compactor"
|
|
2896
3160
|
r")\b"
|
|
2897
3161
|
)
|
|
2898
3162
|
# Match function definitions: function name() or local function name()
|
|
@@ -3167,6 +3431,9 @@ class TactusRuntime:
|
|
|
3167
3431
|
except LuaSandboxError as e:
|
|
3168
3432
|
raise TactusRuntimeError(f"Failed to parse DSL: {e}")
|
|
3169
3433
|
|
|
3434
|
+
lua_globals = sandbox.lua.globals()
|
|
3435
|
+
self._register_assignment_tasks(builder, lua_globals)
|
|
3436
|
+
|
|
3170
3437
|
self._expand_inline_task_children(builder.registry)
|
|
3171
3438
|
|
|
3172
3439
|
# Execute IncludeTasks files to register additional tasks
|
|
@@ -3269,7 +3536,6 @@ class TactusRuntime:
|
|
|
3269
3536
|
# The script mode transformation (in _maybe_transform_script_mode_source)
|
|
3270
3537
|
# is designed to skip files with named function definitions to avoid wrapping
|
|
3271
3538
|
# them incorrectly.
|
|
3272
|
-
lua_globals = sandbox.lua.globals()
|
|
3273
3539
|
if "main" in lua_globals:
|
|
3274
3540
|
main_func = lua_globals["main"]
|
|
3275
3541
|
# Check if it's a function and not already registered
|
|
@@ -3297,6 +3563,49 @@ class TactusRuntime:
|
|
|
3297
3563
|
logger.debug(f"Registry after parsing: lua_tools={list(result.registry.lua_tools.keys())}")
|
|
3298
3564
|
return result.registry
|
|
3299
3565
|
|
|
3566
|
+
def _register_assignment_tasks(self, builder: RegistryBuilder, lua_globals: Any) -> None:
|
|
3567
|
+
if not lua_globals:
|
|
3568
|
+
return
|
|
3569
|
+
|
|
3570
|
+
for key, value in lua_globals.items():
|
|
3571
|
+
if not isinstance(key, str):
|
|
3572
|
+
continue
|
|
3573
|
+
if not hasattr(value, "items"):
|
|
3574
|
+
continue
|
|
3575
|
+
try:
|
|
3576
|
+
marker = value["__tactus_task_config"]
|
|
3577
|
+
except Exception:
|
|
3578
|
+
continue
|
|
3579
|
+
if not marker:
|
|
3580
|
+
continue
|
|
3581
|
+
task_config = lua_table_to_dict(value)
|
|
3582
|
+
if not isinstance(task_config, dict):
|
|
3583
|
+
continue
|
|
3584
|
+
if key in builder.registry.tasks:
|
|
3585
|
+
continue
|
|
3586
|
+
if "entry" in task_config and not callable(task_config["entry"]):
|
|
3587
|
+
raise TactusRuntimeError(f"Task '{key}' entry must be a function")
|
|
3588
|
+
builder.register_task(key, task_config)
|
|
3589
|
+
|
|
3590
|
+
child_sources = task_config.get("__tactus_child_tasks")
|
|
3591
|
+
if isinstance(child_sources, dict):
|
|
3592
|
+
child_iter = child_sources.items()
|
|
3593
|
+
else:
|
|
3594
|
+
child_iter = value.items()
|
|
3595
|
+
for child_key, child_value in child_iter:
|
|
3596
|
+
child_name = child_key if isinstance(child_key, str) else None
|
|
3597
|
+
if not hasattr(child_value, "items"):
|
|
3598
|
+
continue
|
|
3599
|
+
child_config = lua_table_to_dict(child_value)
|
|
3600
|
+
if not child_name and isinstance(child_config, dict):
|
|
3601
|
+
child_name = child_config.get("__task_name")
|
|
3602
|
+
if child_name and isinstance(child_config, dict):
|
|
3603
|
+
if "entry" in child_config and not callable(child_config["entry"]):
|
|
3604
|
+
raise TactusRuntimeError(
|
|
3605
|
+
f"Task '{key}:{child_name}' entry must be a function"
|
|
3606
|
+
)
|
|
3607
|
+
builder.register_task(child_name, child_config, parent=key)
|
|
3608
|
+
|
|
3300
3609
|
def _registry_to_config(self, registry: ProcedureRegistry) -> dict[str, Any]:
|
|
3301
3610
|
"""
|
|
3302
3611
|
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=kZIgMAqEdibTGcY5NARvHI3BnbR8fsjBkiGdbDZ2JBg,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
|
|
@@ -29,7 +29,7 @@ tactus/broker/protocol.py,sha256=v4DFSVoecerqxbqK-vbRfYEAD10tk-QXNH_d9PFgkWg,534
|
|
|
29
29
|
tactus/broker/server.py,sha256=s0_Uokovf5s-IR8Ieb3r1h9dnt4eO_PT0aycwuHwhks,56236
|
|
30
30
|
tactus/broker/stdio.py,sha256=JXkEz-PCU3IQXNkt16YJtYmwkR43eS6CfjxAHc-YCfQ,439
|
|
31
31
|
tactus/cli/__init__.py,sha256=kVhdCkwWEPdt3vn9si-iKvh6M9817aOH6rLSsNzRuyg,80
|
|
32
|
-
tactus/cli/app.py,sha256
|
|
32
|
+
tactus/cli/app.py,sha256=I_89_mis8JFnfJ6yLNFA_PmQrmd79Ci3XZ0Pc0-6MDU,103242
|
|
33
33
|
tactus/cli/control.py,sha256=jCKKy8f6x8wV5im-MwxOtgz85oYLTHhPKXx-3FtRwoU,13364
|
|
34
34
|
tactus/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
35
|
tactus/core/__init__.py,sha256=TK5rWr3HmOO_igFa5ESGp6teWwS58vnvQhIWqkcgqwk,880
|
|
@@ -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=-bTopHqUq6B_CSsnVkeNK7uy-WbJD8n3B4LOCpXGcXM,110492
|
|
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=_egKpvp1qSB0q1zTdcjXOeaLZIEB2oQWoiFxih9K_yw,168471
|
|
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.40.0.dist-info/METADATA,sha256=ZMyLh83LARAXlKw67who3Yfbgkyw5_4ZCJ0PAO3gSTI,60383
|
|
230
|
+
tactus-0.40.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
231
|
+
tactus-0.40.0.dist-info/entry_points.txt,sha256=vWseqty8m3z-Worje0IYxlioMjPDCoSsm0AtY4GghBY,47
|
|
232
|
+
tactus-0.40.0.dist-info/licenses/LICENSE,sha256=ivohBcAIYnaLPQ-lKEeCXSMvQUVISpQfKyxHBHoa4GA,1066
|
|
233
|
+
tactus-0.40.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|