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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# Variables
SPEC_FILE := acp-spec/openapi.json
MANIFEST_SPEC_FILE := manifest-spec/openapi.yaml
MANIFEST_SPEC_FILE := manifest-spec/manifest.json

OUTPUT_DIR := src/agent_workflow_server/generated
OUTPUT_DIR_TMP := src/agent_workflow_server/tmp
Expand Down
6 changes: 4 additions & 2 deletions src/agent_workflow_server/agents/adapters/langgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
BaseAgent,
ThreadsNotSupportedError,
)
from agent_workflow_server.generated.manifest.models.agent_manifest import AgentManifest
from agent_workflow_server.generated.manifest.models.agent_deployment import (
AgentDeployment,
)
from agent_workflow_server.services.message import Message
from agent_workflow_server.services.thread_state import ThreadState
from agent_workflow_server.storage.models import Run
Expand All @@ -25,7 +27,7 @@ class LangGraphAdapter(BaseAdapter):
def load_agent(
self,
agent: object,
manifest: AgentManifest,
manifest: AgentDeployment,
set_thread_persistance_flag: Optional[callable],
) -> Optional[BaseAgent]:
if isinstance(agent, Graph):
Expand Down
12 changes: 7 additions & 5 deletions src/agent_workflow_server/agents/adapters/llamaindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
from pydantic import BaseModel

from agent_workflow_server.agents.base import BaseAdapter, BaseAgent
from agent_workflow_server.generated.manifest.models.agent_manifest import AgentManifest
from agent_workflow_server.generated.manifest.models.agent_deployment import (
AgentDeployment,
)
from agent_workflow_server.services.message import Message
from agent_workflow_server.services.thread_state import ThreadState
from agent_workflow_server.storage.models import Run
Expand All @@ -30,7 +32,7 @@ class LlamaIndexAdapter(BaseAdapter):
def load_agent(
self,
agent: object,
manifest: AgentManifest,
manifest: AgentDeployment,
set_thread_persistance_flag: Optional[callable],
) -> Optional[BaseAgent]:
if callable(agent) and len(inspect.signature(agent).parameters) == 0:
Expand All @@ -49,7 +51,7 @@ def __init__(self, interrupt_event: Event, resume_event: Event):


class LlamaIndexAgent(BaseAgent):
def __init__(self, agent: Workflow, manifest: AgentManifest):
def __init__(self, agent: Workflow, manifest: AgentDeployment):
self.agent = agent
self.manifest = manifest
self.contexts: Dict[str, Dict] = {}
Expand All @@ -59,9 +61,9 @@ def __init__(self, agent: Workflow, manifest: AgentManifest):
self.checkpoints: Dict[str, List[LlamaIndexCheckpoint]] = {}

def _load_interrupts_dict(
self, manifest: AgentManifest
self, manifest: AgentDeployment
) -> Dict[str, InterruptInfo]:
interrupts_info = manifest.deployment.deployment_options[
interrupts_info = manifest.deployment_options[
0
].actual_instance.framework_config.actual_instance.interrupts
interrupts_dict = {}
Expand Down
6 changes: 4 additions & 2 deletions src/agent_workflow_server/agents/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
from abc import ABC, abstractmethod
from typing import Any, AsyncGenerator, List, Optional

from agent_workflow_server.generated.manifest.models.agent_manifest import AgentManifest
from agent_workflow_server.generated.manifest.models.agent_deployment import (
AgentDeployment,
)
from agent_workflow_server.services.message import Message
from agent_workflow_server.services.thread_state import ThreadState
from agent_workflow_server.storage.models import Run
Expand Down Expand Up @@ -48,7 +50,7 @@ class BaseAdapter(ABC):
def load_agent(
self,
agent: Any,
manifest: AgentManifest,
deployment: AgentDeployment,
set_thread_persistance_flag: Optional[callable] = None,
) -> Optional[BaseAgent]:
"""Checks the type of the agent and if it is supported, returns an instance of the agent."""
Expand Down
48 changes: 34 additions & 14 deletions src/agent_workflow_server/agents/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@

import agent_workflow_server.agents.adapters
from agent_workflow_server.agents.oas_generator import generate_agent_oapi
from agent_workflow_server.generated.manifest.models.agent_deployment import (
AgentDeployment,
)
from agent_workflow_server.generated.manifest.models.agent_manifest import AgentManifest
from agent_workflow_server.generated.models.agent import Agent
from agent_workflow_server.generated.models.agent_acp_descriptor import (
AgentACPDescriptor,
)
from agent_workflow_server.generated.models.agent_acp_spec import AgentACPSpec
from agent_workflow_server.generated.models.agent_metadata import AgentMetadata
from agent_workflow_server.generated.models.agent_ref import AgentRef
from agent_workflow_server.generated.models.agent_search_request import (
AgentSearchRequest,
)
Expand All @@ -25,11 +31,30 @@
logger = logging.getLogger(__name__)


def _make_acp_descriptor(manifest: AgentManifest) -> AgentACPDescriptor:
"""Create an AgentACPDescriptor from a AgentManifest"""
if not manifest.extensions or len(manifest.extensions) == 0:
raise ValueError(
"Manifest does not contain any extensions. Cannot create ACP descriptor."
)
return AgentACPDescriptor(
metadata=AgentMetadata(
ref=AgentRef(
name=manifest.name,
version=manifest.version,
url=manifest.locators[0].url,
),
description=manifest.description,
),
specs=AgentACPSpec.model_validate(manifest.extensions[0].data.acp.model_dump()),
)


class AgentInfo(NamedTuple):
agent: BaseAgent
acp_descriptor: AgentACPDescriptor
schema: Mapping[Hashable, Any]
manifest: dict # TODO: proper type from specs
deployment: AgentDeployment


def _load_adapters() -> List[BaseAdapter]:
Expand Down Expand Up @@ -62,17 +87,12 @@ def _load_adapters() -> List[BaseAdapter]:
def _read_manifest(path: str):
if os.path.isfile(path):
with open(path, "r") as file:
try:
manifest_data = json.load(file)
except json.JSONDecodeError as e:
raise ValueError(
f"Invalid JSON format in manifest file: {path}. Error: {e}"
)
manifest_data = json.load(file)
manifest = AgentManifest.model_validate(manifest_data)
# print full path
logger.info(f"Loaded Agent Manifest from {os.path.abspath(path)}")
return AgentACPDescriptor.model_validate(
manifest_data
), AgentManifest.model_validate(manifest_data)
return _make_acp_descriptor(manifest), manifest.extensions[0].data.deployment

return None, None


Expand Down Expand Up @@ -119,8 +139,8 @@ def _resolve_agent(
] + add_manifest_paths

for manifest_path in manifest_paths:
acp_descriptor, manifest = _read_manifest(manifest_path)
if acp_descriptor and manifest:
acp_descriptor, deployment = _read_manifest(manifest_path)
if acp_descriptor and deployment:
break
else:
raise ImportError(
Expand All @@ -138,7 +158,7 @@ def _resolve_agent(

agent = None
for adapter in ADAPTERS:
agent = adapter.load_agent(resolved, manifest, DB.set_persist_threads)
agent = adapter.load_agent(resolved, deployment, DB.set_persist_threads)
if agent is not None:
break
else:
Expand All @@ -155,7 +175,7 @@ def _resolve_agent(
logger.info(f"Agent Type: {type(agent).__name__}")

return AgentInfo(
agent=agent, acp_descriptor=acp_descriptor, manifest=manifest, schema=schema
agent=agent, acp_descriptor=acp_descriptor, deployment=deployment, schema=schema
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class AgentDependency(BaseModel):
""" # noqa: E501
name: StrictStr = Field(description="Name of the agent dependency")
ref: AgentReference
deployment_option: Optional[StrictStr] = Field(default=None, description="Selected deployment option for this agent. ")
deployment_option: Optional[StrictStr] = Field(default=None, description="Selected deployment option for this agent.")
env_var_values: Optional[EnvVarValues] = Field(default=None, description="Environment variable values to be set for this agent.")
__properties: ClassVar[List[str]] = ["name", "ref", "deployment_option", "env_var_values"]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class AgentDeployment(BaseModel):
""" # noqa: E501
deployment_options: List[AgentDeploymentDeploymentOptionsInner] = Field(description="List of possible methods to instantiate or consume the agent. Any of the available option could be used. Every option could be associated with a unique name within this agent. If present, when another manifest refers to this manifest, it can also select the preferred deployment option.")
env_vars: Optional[List[EnvVar]] = Field(default=None, description="List of possible environment variables that the agent may require to be set before it can be used.")
dependencies: Optional[List[AgentDependency]] = Field(default=None, description="List of all other agents this agent depends on")
__properties: ClassVar[List[str]] = ["deployment_options", "env_vars", "dependencies"]
agent_deps: Optional[List[AgentDependency]] = Field(default=None, description="List of all other agents this agent depends on")
__properties: ClassVar[List[str]] = ["deployment_options", "env_vars", "agent_deps"]

model_config = {
"populate_by_name": True,
Expand Down Expand Up @@ -93,13 +93,13 @@ def to_dict(self) -> Dict[str, Any]:
if _item:
_items.append(_item.to_dict())
_dict['env_vars'] = _items
# override the default output from pydantic by calling `to_dict()` of each item in dependencies (list)
# override the default output from pydantic by calling `to_dict()` of each item in agent_deps (list)
_items = []
if self.dependencies:
for _item in self.dependencies:
if self.agent_deps:
for _item in self.agent_deps:
if _item:
_items.append(_item.to_dict())
_dict['dependencies'] = _items
_dict['agent_deps'] = _items
return _dict

@classmethod
Expand All @@ -114,7 +114,7 @@ def from_dict(cls, obj: Dict) -> Self:
_obj = cls.model_validate({
"deployment_options": [AgentDeploymentDeploymentOptionsInner.from_dict(_item) for _item in obj.get("deployment_options")] if obj.get("deployment_options") is not None else None,
"env_vars": [EnvVar.from_dict(_item) for _item in obj.get("env_vars")] if obj.get("env_vars") is not None else None,
"dependencies": [AgentDependency.from_dict(_item) for _item in obj.get("dependencies")] if obj.get("dependencies") is not None else None
"agent_deps": [AgentDependency.from_dict(_item) for _item in obj.get("agent_deps")] if obj.get("agent_deps") is not None else None
})
return _obj

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,31 @@



from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel, ConfigDict, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from agent_workflow_server.generated.manifest.models.agent_acp_specs import AgentACPSpecs
from agent_workflow_server.generated.manifest.models.agent_deployment import AgentDeployment
from agent_workflow_server.generated.manifest.models.agent_metadata import AgentMetadata
from agent_workflow_server.generated.manifest.models.locator import Locator
from agent_workflow_server.generated.manifest.models.manifest import Manifest
from agent_workflow_server.generated.manifest.models.skill import Skill
try:
from typing import Self
except ImportError:
from typing_extensions import Self

class AgentManifest(BaseModel):
"""
Describe all the ACP specs of an agent, including schemas and protocol features.
AgentManifest
""" # noqa: E501
metadata: AgentMetadata
specs: AgentACPSpecs
deployment: Optional[AgentDeployment] = None
__properties: ClassVar[List[str]] = ["metadata", "specs", "deployment"]
annotations: Optional[Dict[str, StrictStr]] = None
authors: List[StrictStr]
created_at: StrictStr
description: StrictStr
extensions: List[Manifest]
locators: List[Locator]
name: StrictStr
schema_version: StrictStr
skills: List[Skill]
version: StrictStr
__properties: ClassVar[List[str]] = ["annotations", "authors", "created_at", "description", "extensions", "locators", "name", "schema_version", "skills", "version"]

model_config = {
"populate_by_name": True,
Expand Down Expand Up @@ -79,15 +86,27 @@ def to_dict(self) -> Dict[str, Any]:
},
exclude_none=True,
)
# override the default output from pydantic by calling `to_dict()` of metadata
if self.metadata:
_dict['metadata'] = self.metadata.to_dict()
# override the default output from pydantic by calling `to_dict()` of specs
if self.specs:
_dict['specs'] = self.specs.to_dict()
# override the default output from pydantic by calling `to_dict()` of deployment
if self.deployment:
_dict['deployment'] = self.deployment.to_dict()
# override the default output from pydantic by calling `to_dict()` of each item in extensions (list)
_items = []
if self.extensions:
for _item in self.extensions:
if _item:
_items.append(_item.to_dict())
_dict['extensions'] = _items
# override the default output from pydantic by calling `to_dict()` of each item in locators (list)
_items = []
if self.locators:
for _item in self.locators:
if _item:
_items.append(_item.to_dict())
_dict['locators'] = _items
# override the default output from pydantic by calling `to_dict()` of each item in skills (list)
_items = []
if self.skills:
for _item in self.skills:
if _item:
_items.append(_item.to_dict())
_dict['skills'] = _items
return _dict

@classmethod
Expand All @@ -100,9 +119,16 @@ def from_dict(cls, obj: Dict) -> Self:
return cls.model_validate(obj)

_obj = cls.model_validate({
"metadata": AgentMetadata.from_dict(obj.get("metadata")) if obj.get("metadata") is not None else None,
"specs": AgentACPSpecs.from_dict(obj.get("specs")) if obj.get("specs") is not None else None,
"deployment": AgentDeployment.from_dict(obj.get("deployment")) if obj.get("deployment") is not None else None
"annotations": obj.get("annotations"),
"authors": obj.get("authors"),
"created_at": obj.get("created_at"),
"description": obj.get("description"),
"extensions": [Manifest.from_dict(_item) for _item in obj.get("extensions")] if obj.get("extensions") is not None else None,
"locators": [Locator.from_dict(_item) for _item in obj.get("locators")] if obj.get("locators") is not None else None,
"name": obj.get("name"),
"schema_version": obj.get("schema_version"),
"skills": [Skill.from_dict(_item) for _item in obj.get("skills")] if obj.get("skills") is not None else None,
"version": obj.get("version")
})
return _obj

Expand Down
Loading