mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
feat: refactor node spec and add mcp tools (#244)
* refactor: carve out extraction panel * refactor: create spec versions for node types * refactor: create a GenericNode and remove custom nodes * feat: add python and typescript sdk * add dograh sdk * fix: fetch draft workflow definition over published one * fix: fix routes of SDKs to use code gen * chore: remove doclink dependency to reduce image size * chore: format files * chore: bump pipecat * feat: let mcp fetch archived workflows on demand * chore: fix tests * feat: add sdk documentation * chore: change banner and add badge
This commit is contained in:
parent
0a61ef295f
commit
00a1a22b74
162 changed files with 14355 additions and 3554 deletions
99
api/tests/test_sdk_sync.py
Normal file
99
api/tests/test_sdk_sync.py
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
"""Drift guard: committed SDK typed files must match what codegen
|
||||
produces from the current `node_specs/` registry.
|
||||
|
||||
Fails loudly if a spec was edited without running
|
||||
`./scripts/generate_sdk.sh`. CI also runs the full script and asserts
|
||||
an empty `git diff` as the authoritative cross-language check; this
|
||||
test is the fast local feedback loop inside pytest.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
# Ensure the Python SDK package is importable without requiring a
|
||||
# `pip install -e sdk/python`. The codegen lives there because it ships
|
||||
# with the SDK wheel, but tests need to reach it directly.
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
SDK_PY_SRC = REPO_ROOT / "sdk" / "python" / "src"
|
||||
if str(SDK_PY_SRC) not in sys.path:
|
||||
sys.path.insert(0, str(SDK_PY_SRC))
|
||||
|
||||
from dograh_sdk.codegen import generate_all # noqa: E402
|
||||
|
||||
from api.services.workflow.node_specs import SPEC_VERSION, all_specs # noqa: E402
|
||||
|
||||
PY_OUT = REPO_ROOT / "sdk" / "python" / "src" / "dograh_sdk" / "typed"
|
||||
TS_OUT = REPO_ROOT / "sdk" / "typescript" / "src" / "typed"
|
||||
TS_CODEGEN = REPO_ROOT / "sdk" / "typescript" / "scripts" / "codegen.mts"
|
||||
REGEN_HINT = "Run ./scripts/generate_sdk.sh to regenerate."
|
||||
|
||||
|
||||
def _specs_payload() -> dict:
|
||||
return {
|
||||
"spec_version": SPEC_VERSION,
|
||||
"node_types": [s.model_dump(mode="json") for s in all_specs()],
|
||||
}
|
||||
|
||||
|
||||
def _compare_trees(expected_dir: Path, actual_dir: Path, *, skip: set[str]) -> None:
|
||||
def tree(d: Path) -> dict[str, str]:
|
||||
return {
|
||||
p.name: p.read_text()
|
||||
for p in d.iterdir()
|
||||
if p.is_file() and p.name not in skip
|
||||
}
|
||||
|
||||
expected = tree(expected_dir)
|
||||
actual = tree(actual_dir)
|
||||
|
||||
if expected.keys() != actual.keys():
|
||||
pytest.fail(
|
||||
f"File set differs in {expected_dir.name}/.\n"
|
||||
f" committed: {sorted(expected)}\n"
|
||||
f" generated: {sorted(actual)}\n"
|
||||
f"{REGEN_HINT}"
|
||||
)
|
||||
for name in sorted(expected):
|
||||
if expected[name] != actual[name]:
|
||||
pytest.fail(
|
||||
f"{expected_dir.name}/{name} is out of sync with node_specs. "
|
||||
f"{REGEN_HINT}"
|
||||
)
|
||||
|
||||
|
||||
def test_python_sdk_typed_in_sync(tmp_path: Path) -> None:
|
||||
specs = _specs_payload()["node_types"]
|
||||
generate_all(specs, tmp_path)
|
||||
# _base.py is hand-written and lives alongside generated files.
|
||||
_compare_trees(PY_OUT, tmp_path, skip={"_base.py", "__pycache__"})
|
||||
|
||||
|
||||
@pytest.mark.skipif(shutil.which("node") is None, reason="node binary not available")
|
||||
def test_typescript_sdk_typed_in_sync(tmp_path: Path) -> None:
|
||||
specs_file = tmp_path / "specs.json"
|
||||
specs_file.write_text(json.dumps(_specs_payload()))
|
||||
out = tmp_path / "ts_out"
|
||||
|
||||
result = subprocess.run(
|
||||
[
|
||||
"node",
|
||||
str(TS_CODEGEN),
|
||||
"--input",
|
||||
str(specs_file),
|
||||
"--out",
|
||||
str(out),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
assert result.returncode == 0, (
|
||||
f"TS codegen failed:\nstdout: {result.stdout}\nstderr: {result.stderr}"
|
||||
)
|
||||
_compare_trees(TS_OUT, out, skip=set())
|
||||
Loading…
Add table
Add a link
Reference in a new issue