Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion api/oss/src/apis/fastapi/applications/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, List
from typing import Any, Dict, Optional, List

from pydantic import BaseModel, Field

Expand Down Expand Up @@ -599,6 +599,49 @@ class SimpleApplicationQueryRequest(BaseModel):
)


class AgentTemplateOverlay(BaseModel):
"""A documented subset of the `parameters.agent` authoring shape.

Carries the platform-owned tools, authoring skills, and sandbox elevation the playground
layers on top of the draft for the build kit. Entries are intentionally open (platform-op
configs and `@ag.embed` references), so they are typed loosely: the full `parameters.agent`
authoring template has no shared Pydantic model today (it rides as free-form
`data.parameters`), and the SDK's runtime `AgentTemplate` is the flattened parse with
different field names, so neither can be reused 1:1 to type this overlay.
"""

tools: List[Dict[str, Any]] = Field(
default_factory=list,
description="Platform tool configs and `@ag.embed` tool references.",
)
skills: List[Dict[str, Any]] = Field(
default_factory=list,
description="`@ag.embed` references to authoring skills.",
)
sandbox: Optional[Dict[str, Any]] = Field(
default=None,
description="Sandbox section overlay, e.g. `{permissions: {...}}`.",
)


class PlaygroundBuildKitContext(BaseModel):
"""Read-only playground build-kit context for one inspect/fetch response."""

agent_template_overlay: Optional[AgentTemplateOverlay] = Field(
default=None,
description="Partial `parameters.agent` overlay applied by the playground only.",
)


class SimpleApplicationAdditionalContext(BaseModel):
"""Platform-supplied read-only context for a simple-application response."""

playground_build_kit: Optional[PlaygroundBuildKitContext] = Field(
default=None,
description="Playground-only build kit data that is never persisted on the app.",
)


class SimpleApplicationResponse(BaseModel):
"""Simple-application single-row response envelope."""

Expand All @@ -613,6 +656,10 @@ class SimpleApplicationResponse(BaseModel):
"revision's `data` merged. `data.url` is the invocation URL."
),
)
additional_context: Optional[SimpleApplicationAdditionalContext] = Field(
default=None,
description="Read-only platform context derived for this response.",
)


class SimpleApplicationsResponse(BaseModel):
Expand Down
92 changes: 92 additions & 0 deletions api/oss/src/apis/fastapi/applications/overlay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Read-only overlays attached to application inspect/fetch responses."""

from typing import Any, Dict, List, Optional

from agenta.sdk.agents.adapters.agenta_builtins import GETTING_STARTED_WITH_AGENTA_SLUG
from agenta.sdk.agents.platform.op_catalog import PLATFORM_OPS

from oss.src.core.workflows.static_catalog import (
STATIC_SLUG_PREFIX,
StaticWorkflowCatalog,
_STATIC_WORKFLOWS,
)


def _workflow_embed(
slug: str,
*,
name: Optional[str],
selector_path: str,
) -> Dict[str, Any]:
# The selector is load-bearing: without it the embed resolves to the whole revision.data
# (``{uri, parameters: {skill|tool: ...}}``), which neither the SDK skill parser nor the tool
# coercer accepts. ``parameters.skill`` / ``parameters.tool`` extracts the flat inline value
# the agent template expects (see test_skill_template_catalog canonical embed shape).
embed: Dict[str, Any] = {
"@ag.embed": {
"@ag.references": {"workflow": {"slug": slug}},
"@ag.selector": {"path": selector_path},
}
}
# The display name rides alongside the embed so the playground shows the workflow's name, not
# the raw ``__ag__*`` slug. Resolution replaces the whole entry, so this sibling is discarded
# before the tool/skill parser ever sees it.
if name:
embed["name"] = name
return embed


def _reserved_static_tool_embeds(
catalog: StaticWorkflowCatalog,
) -> List[Dict[str, Any]]:
"""Tool embeds for the reserved static workflows that are tools (not skills).

Only confirmed non-skill static workflows with a resolvable revision are included; a missing
revision or missing flags is skipped so an invalid tool embed can't leak into the playground.
"""
embeds: List[Dict[str, Any]] = []
for slug in _STATIC_WORKFLOWS:
if not slug.startswith(STATIC_SLUG_PREFIX):
continue
revision = catalog.retrieve_revision(slug=slug)
if not revision or not revision.flags or revision.flags.is_skill:
continue
embeds.append(
_workflow_embed(
slug,
name=revision.name,
selector_path="parameters.tool",
)
)
return embeds


def build_agent_template_overlay() -> Dict[str, Any]:
"""Build the playground-only agent-template overlay from platform-owned sources."""

catalog = StaticWorkflowCatalog()

skills: List[Dict[str, Any]] = []
authoring_skill = catalog.retrieve_revision(slug=GETTING_STARTED_WITH_AGENTA_SLUG)
if authoring_skill:
skills.append(
_workflow_embed(
GETTING_STARTED_WITH_AGENTA_SLUG,
name=authoring_skill.name,
selector_path="parameters.skill",
)
)

return {
"tools": [
*[{"type": "platform", "op": op_name} for op_name in PLATFORM_OPS],
*_reserved_static_tool_embeds(catalog),
],
"skills": skills,
"sandbox": {
"permissions": {
"write_files": "allow",
"execute_code": "allow",
}
},
}
22 changes: 22 additions & 0 deletions api/oss/src/apis/fastapi/applications/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@
SimpleApplicationCreateRequest,
SimpleApplicationEditRequest,
SimpleApplicationQueryRequest,
SimpleApplicationAdditionalContext,
SimpleApplicationResponse,
SimpleApplicationsResponse,
PlaygroundBuildKitContext,
)
from oss.src.apis.fastapi.applications.overlay import build_agent_template_overlay
from oss.src.apis.fastapi.applications.utils import (
parse_application_variant_query_request_from_params,
parse_application_variant_query_request_from_body,
Expand Down Expand Up @@ -1902,9 +1905,28 @@ async def fetch_simple_application(
application_id=application_id,
)

# Build the read-only playground overlay defensively: this handler returns a controlled
# default on error (``@suppress_exceptions``), so letting overlay synthesis raise would
# blank the whole fetched application instead of just dropping the optional context.
additional_context = None
if simple_application:
try:
additional_context = SimpleApplicationAdditionalContext(
playground_build_kit=PlaygroundBuildKitContext(
agent_template_overlay=build_agent_template_overlay(),
),
)
except Exception: # noqa: BLE001 - overlay is best-effort; never blank the response
log.warning(
"Failed to build playground build-kit overlay for application %s",
application_id,
exc_info=True,
)

simple_application_response = SimpleApplicationResponse(
count=1 if simple_application else 0,
application=simple_application,
additional_context=additional_context,
)

return simple_application_response
Expand Down
24 changes: 24 additions & 0 deletions api/oss/src/core/workflows/static_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@
from uuid import UUID, uuid5

from agenta.sdk.agents.adapters.agenta_builtins import (
BUILD_YOUR_FIRST_APP_SKILL,
BUILD_YOUR_FIRST_APP_SLUG,
DISCOVER_AND_WIRE_TOOLS_SKILL,
DISCOVER_AND_WIRE_TOOLS_SLUG,
GETTING_STARTED_WITH_AGENTA_SKILL,
GETTING_STARTED_WITH_AGENTA_SLUG,
SET_UP_TRIGGERS_SKILL,
SET_UP_TRIGGERS_SLUG,
)
from agenta.sdk.agents.platform.workflow import (
REQUEST_CONNECTION_TOOL_NAME,
Expand Down Expand Up @@ -121,6 +127,24 @@ def _client_tool_revision() -> WorkflowRevision:
"v1": _client_tool_revision(),
},
},
BUILD_YOUR_FIRST_APP_SLUG: {
"latest": "v1",
"versions": {
"v1": _skill_revision(BUILD_YOUR_FIRST_APP_SKILL),
},
},
DISCOVER_AND_WIRE_TOOLS_SLUG: {
"latest": "v1",
"versions": {
"v1": _skill_revision(DISCOVER_AND_WIRE_TOOLS_SKILL),
},
},
SET_UP_TRIGGERS_SLUG: {
"latest": "v1",
"versions": {
"v1": _skill_revision(SET_UP_TRIGGERS_SKILL),
},
},
Comment on lines +130 to +147

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Gate these static entries until the trigger/tool surface exists.

These slugs become live as soon as they land in _STATIC_WORKFLOWS, but the paired skill content still directs the model into follow-up-only tool calls. In particular, build-your-first-app routes users to set-up-triggers, and that skill assumes find_triggers, create_schedule, create_subscription, test_subscription, and list_deliveries already exist. Shipping the catalog entries now turns those dead ends into runtime behavior for reserved __ag__* slugs. Keep them out of the static catalog until the tools land, or gate resolution behind availability.

}


Expand Down
Loading
Loading