Skip to content

Commit 83d4841

Browse files
committed
feat(io-mapper): io mapping from manifests
1 parent 20f9320 commit 83d4841

21 files changed

+956
-512
lines changed

Diff for: agntcy_iomapper/__init__.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates.
22
# SPDX-License-Identifier: Apache-2.0
33
# ruff: noqa: F401
4-
from .agent_iomapper import (
4+
from .base import (
55
AgentIOMapper,
66
AgentIOMapperConfig,
77
AgentIOMapperInput,
88
AgentIOMapperOutput,
9-
)
10-
from .base import (
119
BaseIOMapper,
1210
BaseIOMapperConfig,
1311
BaseIOMapperInput,

Diff for: agntcy_iomapper/base/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates.
2+
# SPDX-License-Identifier: Apache-2.0
3+
from .agent_iomapper import *
4+
from .base import *
File renamed without changes.

Diff for: agntcy_iomapper/base.py renamed to agntcy_iomapper/base/base.py

+49-4
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,62 @@
99

1010

1111
class ArgumentsDescription(BaseModel):
12+
"""
13+
ArgumentsDescription a pydantic model that defines
14+
the details necessary to perfom io mapping between two agents
15+
"""
16+
1217
json_schema: Schema | None = Field(
1318
default=None, description="Data format JSON schema"
1419
)
1520
description: str | None = Field(
1621
default=None, description="Data (semantic) natural language description"
1722
)
23+
agent_manifest: dict[str, Any] | None = Field(
24+
default=None,
25+
description="Agent Manifest definition as per https://supreme-adventure-wg7nnwm.pages.github.io/docs/openapi.html#model/agentmanifest",
26+
)
1827

1928
@model_validator(mode="after")
2029
def _validate_obj(self) -> Self:
21-
if self.json_schema is None and self.description is None:
30+
if (
31+
self.json_schema is None
32+
and self.description is None
33+
and self.agent_manifest
34+
):
2235
raise ValueError(
23-
'Either the "schema" field and/or the "description" field must be specified.'
36+
'Either the "schema" field and/or the "description" or agent_manifest field must be specified.'
2437
)
2538
return self
2639

2740

2841
class BaseIOMapperInput(BaseModel):
29-
input: ArgumentsDescription = Field(description="Input data descriptions")
30-
output: ArgumentsDescription = Field(description="Output data descriptions")
42+
input: ArgumentsDescription = Field(
43+
description="Input data descriptions",
44+
)
45+
output: ArgumentsDescription = Field(
46+
description="Output data descriptions",
47+
)
3148
data: Any = Field(description="Data to translate")
3249

50+
@model_validator(mode="after")
51+
def _validate_obj(self) -> Self:
52+
if self.input.agent_manifest is not None:
53+
# given an input agents manifest map its ouput definition
54+
# because the data to be mapped is the result of calling the input agent
55+
self.input.json_schema = Schema.model_validate(
56+
self.input.agent_manifest["specs"]["output"]
57+
)
58+
59+
if self.output.agent_manifest:
60+
# given an output agents manifest map its input definition
61+
# because the data to be mapped would be mapped to it's input
62+
self.output.json_schema = Schema.model_validate(
63+
self.output.agent_manifest["specs"]["input"]
64+
)
65+
66+
return self
67+
3368

3469
class BaseIOMapperOutput(BaseModel):
3570
data: Any = Field(default=None, description="Data after translation")
@@ -73,3 +108,13 @@ async def ainvoke(self, input: BaseIOMapperInput) -> BaseIOMapperOutput:
73108
Args:
74109
input: the data to be mapped
75110
"""
111+
112+
113+
class _ArgumentsDescription:
114+
@staticmethod
115+
def input_args_description(data):
116+
return ArgumentsDescription(is_source_agent=True, **data)
117+
118+
@staticmethod
119+
def output_args_description(data):
120+
return ArgumentsDescription(is_source_agent=False, **data)

Diff for: agntcy_iomapper/imperative/__init__.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates.
2+
# SPDX-License-Identifier: Apache-2.0"
3+
4+
from .imperative import (
5+
ImperativeIOMapper,
6+
ImperativeIOMapperInput,
7+
ImperativeIOMapperOutput,
8+
)

Diff for: agntcy_iomapper/imperative.py renamed to agntcy_iomapper/imperative/imperative.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import jsonschema
2323
from jsonpath_ng.ext import parse
2424

25-
from .base import (
25+
from agntcy_iomapper.base import (
2626
BaseIOMapper,
2727
BaseIOMapperConfig,
2828
BaseIOMapperInput,

Diff for: agntcy_iomapper/langgraph/__init__.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates.
2+
# SPDX-License-Identifier: Apache-2.0"
3+
4+
from .langgraph import (
5+
LangGraphIOMapper,
6+
LangGraphIOMapperConfig,
7+
LangGraphIOMapperInput,
8+
LangGraphIOMapperOutput,
9+
)
10+
11+
from .create_langraph_iomapper import create_langraph_iomapper
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates.
2+
# SPDX-License-Identifier: Apache-2.0"
3+
from langchain_core.runnables import Runnable
4+
5+
from .langgraph import (
6+
LangGraphIOMapper,
7+
LangGraphIOMapperConfig,
8+
LangGraphIOMapperInput,
9+
LangGraphIOMapperOutput,
10+
)
11+
12+
13+
def create_langraph_iomapper(
14+
config: LangGraphIOMapperConfig,
15+
) -> Runnable[LangGraphIOMapperInput, LangGraphIOMapperOutput]:
16+
"""Creates a langgraph agent
17+
Args:
18+
config: The configuration of the llm that would be used during the mapping
19+
Returns:
20+
A runnable representing an agent. It returns as output the mapping result
21+
"""
22+
return LangGraphIOMapper(config).as_runnable()

Diff for: agntcy_iomapper/langgraph.py renamed to agntcy_iomapper/langgraph/langgraph.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
from langchain.chat_models import init_chat_model
77
from langchain_core.language_models import BaseChatModel
8-
from langchain_core.runnables import Runnable, RunnableConfig
8+
from langchain_core.runnables import RunnableConfig
99
from langgraph.utils.runnable import RunnableCallable
1010
from pydantic import Field
1111

12-
from .agent_iomapper import (
12+
from agntcy_iomapper.base import (
1313
AgentIOMapper,
1414
AgentIOMapperConfig,
1515
AgentIOMapperInput,
@@ -86,9 +86,3 @@ def invoke(self, state: dict[str, Any], config: RunnableConfig) -> dict:
8686

8787
def as_runnable(self):
8888
return RunnableCallable(self.invoke, self.ainvoke, name="extract", trace=False)
89-
90-
91-
def create_iomapper(
92-
config: LangGraphIOMapperConfig,
93-
) -> Runnable[LangGraphIOMapperInput, LangGraphIOMapperOutput]:
94-
return LangGraphIOMapper(config).as_runnable()

Diff for: agntcy_iomapper/pydantic_ai.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from pydantic_ai.models.openai import OpenAIModel
1919
from typing_extensions import Self
2020

21-
from .agent_iomapper import (
21+
from agntcy_iomapper.base import (
2222
AgentIOMapper,
2323
AgentIOMapperConfig,
2424
AgentIOMapperInput,

Diff for: docs/usage.md

+15-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Usage
22

3-
The Agntcy IO Mapper functions provided an easy to use package for mapping output from
4-
one agent to another. The data can be described in JSON or with natural language. The
3+
The Agntcy IO Mapper functions provided an easy to use package for mapping output from
4+
one agent to another. The data can be described in JSON or with natural language. The
55
(incomplete) [pydantic](https://docs.pydantic.dev/latest/) models follow:
66

77
```python
@@ -23,16 +23,16 @@ class BaseIOMapperConfig(BaseModel):
2323
validate_json_output: bool = Field(description="Validate output against JSON schema.")
2424
```
2525

26-
There are several different ways to leverage the IO Mapper functions in Python. There
27-
is an [agentic interface](#use-agent-io-mapper) using models that can be invoked on
28-
different AI platforms and a [imperative interface](#use-imperative--deterministic-io-mapper)
26+
There are several different ways to leverage the IO Mapper functions in Python. There
27+
is an [agentic interface](#use-agent-io-mapper) using models that can be invoked on
28+
different AI platforms and a [imperative interface](#use-imperative--deterministic-io-mapper)
2929
that does deterministic JSON remapping without using any AI models.
3030

3131
## Use Agent IO Mapper
3232

33-
The Agent IO Mapper uses an LLM/model to transform the inputs (typically output of the
33+
The Agent IO Mapper uses an LLM/model to transform the inputs (typically output of the
3434
first agent) to match the desired output (typically the input of a second agent). As such,
35-
it additionally supports specifying the model prompts for the translation. The configuration
35+
it additionally supports specifying the model prompts for the translation. The configuration
3636
object provides a specification for the system and default user prompts:
3737

3838
```python
@@ -54,11 +54,11 @@ class AgentIOMapperInput(BaseIOMapperInput):
5454
)
5555
```
5656

57-
Further specification of models and their arguments is left to the underlying supported
57+
Further specification of models and their arguments is left to the underlying supported
5858
packages:
59-
* [Pydantic-AI](#pydantic-ai)
60-
* [LangGraph](#langgraph)
6159

60+
- [Pydantic-AI](#pydantic-ai)
61+
- [LangGraph](#langgraph)
6262

6363
### Pydantic-AI
6464

@@ -70,8 +70,8 @@ This project supports specifying model interations using [LangGraph](https://lan
7070

7171
## Use Imperative / Deterministic IO Mapper
7272

73-
The code snippet below illustrates a fully functional deterministic mapping that
74-
transforms the output of one agent into input for a second agent. The code for the
73+
The code snippet below illustrates a fully functional deterministic mapping that
74+
transforms the output of one agent into input for a second agent. The code for the
7575
agents is omitted.
7676

7777
```python
@@ -89,7 +89,7 @@ agents is omitted.
8989
}
9090
}
9191

92-
# the mapping object using jsonpath, note: the value of the mapping
92+
# the mapping object using jsonpath, note: the value of the mapping
9393
# can be either a jsonpath or a function
9494
mapping_object = {
9595
"prof_question": "$.question",
@@ -115,8 +115,8 @@ agents is omitted.
115115

116116
### Use Examples
117117

118-
1. To run the examples we strongly recommend that a
119-
[virtual environment is created](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/)
118+
1. To run the examples we strongly recommend that a
119+
[virtual environment is created](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/)
120120
2. Install the requirements file
121121
3. From within examples folder run:
122122

0 commit comments

Comments
 (0)