mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-22 08:38:13 +02:00
feat: add Tuner Integration to Dograh (#311)
* Add tuner integration * bump pipecat version * chore: update pipecat submodule to match upstream and use tuner-pipecat-sdk 0.2.0 Update pipecat submodule from 0.0.109.dev23 to 13e98d0d9 (the exact commit upstream dograh-hq/dograh uses after v1.30.1). This installs pipecat-ai as 1.1.0.post277 via setuptools_scm, satisfying tuner-pipecat-sdk 0.2.0's pipecat-ai>=1.0.0 requirement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * wire tuner * feat: refactor integrations into self contained packages * chore: simplify ensure_public_access_token * fix: remove NodeSpec and make DTOs the source of truth * feat: send relevant signal to mcp using to_mcp_dict * fix: fix tests * cleanup: remove nango integrations * feat: add agents.md for integrations --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
This commit is contained in:
parent
afa78fe859
commit
5f28c1b2a9
93 changed files with 3388 additions and 3414 deletions
|
|
@ -1,9 +1,9 @@
|
|||
"""Spec schema for node definitions.
|
||||
|
||||
A `NodeSpec` is the single source of truth for a node type. It drives:
|
||||
- Pydantic validation (the per-type DTOs in dto.py mirror these property types)
|
||||
- The generic UI renderer (frontend reads specs via /api/v1/node-types)
|
||||
- The LLM SDK (constructors and JSON-Schema derived from these specs)
|
||||
`NodeSpec` is the serialized contract exposed to the frontend, MCP tools, and
|
||||
SDKs. Core workflow node specs are generated from the DTO models plus
|
||||
model-attached metadata; integration packages may generate them the same way or
|
||||
register a prebuilt spec object.
|
||||
|
||||
Every property's `description` is LLM-readable copy — treat it as production
|
||||
documentation, not internal notes. Spec lint enforces non-empty descriptions
|
||||
|
|
@ -122,6 +122,16 @@ class PropertyOption(BaseModel):
|
|||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
def to_mcp_dict(self) -> dict[str, Any]:
|
||||
"""Lean projection for `get_node_type`: the `value` an LLM writes in
|
||||
code, plus a `description` when one carries real meaning. The UI
|
||||
`label` is dropped — it's the option's display string, never used
|
||||
when authoring."""
|
||||
out: dict[str, Any] = {"value": self.value}
|
||||
if self.description:
|
||||
out["description"] = self.description
|
||||
return out
|
||||
|
||||
|
||||
class PropertySpec(BaseModel):
|
||||
"""Single field on a node.
|
||||
|
|
@ -175,6 +185,43 @@ class PropertySpec(BaseModel):
|
|||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
def to_mcp_dict(self) -> dict[str, Any]:
|
||||
"""Lean projection of this property for the `get_node_type` MCP tool.
|
||||
|
||||
Keeps only what an LLM needs to author a valid value: name, type,
|
||||
description, llm_hint, requiredness, default, enum options, nested
|
||||
row properties, and validation bounds. UI-rendering concerns
|
||||
(`display_name`, `placeholder`, `display_options`, `editor`,
|
||||
`extra`) and null/empty fields are omitted — they're noise in the
|
||||
model's context and never appear in authored SDK code.
|
||||
"""
|
||||
out: dict[str, Any] = {
|
||||
"name": self.name,
|
||||
"type": self.type.value,
|
||||
"description": self.description,
|
||||
}
|
||||
if self.llm_hint:
|
||||
out["llm_hint"] = self.llm_hint
|
||||
if self.required:
|
||||
out["required"] = True
|
||||
if self.default is not None:
|
||||
out["default"] = self.default
|
||||
if self.options:
|
||||
out["options"] = [opt.to_mcp_dict() for opt in self.options]
|
||||
if self.properties:
|
||||
out["properties"] = [prop.to_mcp_dict() for prop in self.properties]
|
||||
for constraint in (
|
||||
"min_value",
|
||||
"max_value",
|
||||
"min_length",
|
||||
"max_length",
|
||||
"pattern",
|
||||
):
|
||||
value = getattr(self, constraint)
|
||||
if value is not None:
|
||||
out[constraint] = value
|
||||
return out
|
||||
|
||||
|
||||
PropertySpec.model_rebuild()
|
||||
|
||||
|
|
@ -222,3 +269,33 @@ class NodeSpec(BaseModel):
|
|||
graph_constraints: Optional[GraphConstraints] = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
def to_mcp_dict(self) -> dict[str, Any]:
|
||||
"""Lean projection of this spec for the `get_node_type` MCP tool.
|
||||
|
||||
Drops node-level UI metadata (`display_name`, `category`, `icon`,
|
||||
`version`) and the per-property rendering concerns trimmed by
|
||||
`PropertySpec.to_mcp_dict`, leaving just the authoring-relevant
|
||||
schema the LLM consumes when composing a workflow. The full spec is
|
||||
still served verbatim to the frontend renderer (REST `node-types`
|
||||
route) and the SDK codegen / TS validator (`ts_bridge`), which need
|
||||
the dropped fields.
|
||||
"""
|
||||
out: dict[str, Any] = {
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
}
|
||||
if self.llm_hint:
|
||||
out["llm_hint"] = self.llm_hint
|
||||
out["properties"] = [prop.to_mcp_dict() for prop in self.properties]
|
||||
if self.examples:
|
||||
out["examples"] = [
|
||||
ex.model_dump(mode="json", exclude_none=True) for ex in self.examples
|
||||
]
|
||||
if self.graph_constraints:
|
||||
constraints = self.graph_constraints.model_dump(
|
||||
mode="json", exclude_none=True
|
||||
)
|
||||
if constraints:
|
||||
out["graph_constraints"] = constraints
|
||||
return out
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue