Skip to content

Commit 45b5dbf

Browse files
committed
feat(llama_index): add support for llama-index workflow, refactor, improve examples
1 parent 349c5ba commit 45b5dbf

34 files changed

+1015
-698
lines changed

Diff for: Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ setup: set_python_env
4343

4444
# ============================
4545

46-
setup_test:
46+
setup_test: setup
4747
@poetry install --with=test --all-extras
4848

4949
test: setup_test
@@ -57,5 +57,5 @@ test_langgraph_agent: setup_test
5757

5858
test_langgraph_software: setup_test
5959
poetry run pytest tests/test_langgraph_graph_with_io_mapper.py
60-
unittest:
60+
unittest: setup_test
6161
poetry run pytest tests/unittests -vvrx

Diff for: agntcy_iomapper/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates.
22
# SPDX-License-Identifier: Apache-2.0
33
# ruff: noqa: F401
4-
from agntcy_iomapper.agent import FieldMetadata, IOMappingAgent, IOMappingAgentMetadata
4+
from agntcy_iomapper.agent import IOMappingAgent
5+
from agntcy_iomapper.base import FieldMetadata, IOMappingAgentMetadata
56
from agntcy_iomapper.imperative import (
67
ImperativeIOMapper,
78
ImperativeIOMapperInput,
89
ImperativeIOMapperOutput,
910
)
11+
from agntcy_iomapper.llamaindex import IOMappingInputEvent, IOMappingOutputEvent
1012

1113
__all__ = [
1214
"IOMappingAgent",
1315
"IOMappingAgentMetadata",
16+
"IOMappingOutputEvent",
17+
"IOMappingInputEvent",
1418
"ImperativeIOMapper",
1519
"ImperativeIOMapperInput",
1620
"ImperativeIOMapperOutput",

Diff for: agntcy_iomapper/agent/__init__.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,15 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
from agntcy_iomapper.agent.agent_io_mapper import IOMappingAgent
5-
from agntcy_iomapper.agent.base import AgentIOMapper
6-
from agntcy_iomapper.agent.models import (
7-
AgentIOMapperConfig,
8-
AgentIOMapperInput,
9-
AgentIOMapperOutput,
5+
from agntcy_iomapper.base.models import (
6+
BaseIOMapperConfig,
107
FieldMetadata,
118
IOMappingAgentMetadata,
129
)
1310

1411
__all__ = [
15-
"AgentIOMapper",
16-
"AgentIOMapperOutput",
17-
"AgentIOMapperConfig",
12+
"BaseIOMapperConfig",
1813
"IOMappingAgent",
1914
"IOMappingAgentMetadata",
20-
"AgentIOMapperInput",
2115
"FieldMetadata",
2216
]

Diff for: agntcy_iomapper/agent/agent_io_mapper.py

+94-41
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,58 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
import logging
5-
from typing import Any, Optional, Tuple, Union
5+
from typing import Any, Callable, List, Optional, Union
66

77
from langchain_core.language_models import BaseChatModel
88
from langchain_core.runnables import Runnable
9-
from openapi_pydantic import Schema
9+
from llama_index.core.base.llms.base import BaseLLM
10+
from llama_index.core.tools import BaseTool
11+
from llama_index.core.workflow import (
12+
Workflow,
13+
)
1014
from pydantic import BaseModel, Field, model_validator
1115
from typing_extensions import Self
1216

13-
from agntcy_iomapper.agent.models import (
17+
from agntcy_iomapper.base.models import (
1418
AgentIOMapperInput,
19+
ArgumentsDescription,
1520
FieldMetadata,
1621
IOMappingAgentMetadata,
1722
)
18-
from agntcy_iomapper.base import ArgumentsDescription
19-
from agntcy_iomapper.base.utils import create_type_from_schema, extract_nested_fields
23+
from agntcy_iomapper.base.utils import extract_nested_fields, get_io_types
24+
from agntcy_iomapper.imperative import (
25+
ImperativeIOMapper,
26+
ImperativeIOMapperInput,
27+
)
2028
from agntcy_iomapper.langgraph import LangGraphIOMapper, LangGraphIOMapperConfig
29+
from agntcy_iomapper.llamaindex.llamaindex import (
30+
LLamaIndexIOMapper,
31+
)
2132

2233
logger = logging.getLogger(__name__)
2334

2435

2536
class IOMappingAgent(BaseModel):
26-
metadata: IOMappingAgentMetadata = Field(
37+
"""This class exposes all
38+
The IOMappingAgent class is designed for developers building sophisticated multi-agent software that require seamless integration and interaction between
39+
the different agents and workflow steps.
40+
By utilizing the methods provided, developers can construct complex workflows and softwares.
41+
The IOMappingAgent class is intended to serve as a foundational component in applications requiring advanced IO mapping agents in multi-agent systems.
42+
"""
43+
44+
metadata: Optional[IOMappingAgentMetadata] = Field(
2745
...,
2846
description="Details about the fields to be used in the translation and about the output",
2947
)
30-
llm: Optional[Union[BaseChatModel, str]] = (
31-
Field(
32-
None,
33-
description="Model to use for translation as LangChain description or model class.",
34-
),
48+
llm: Optional[Union[BaseChatModel, str]] = Field(
49+
None,
50+
description="Model to use for translation as LangChain description or model class.",
3551
)
3652

3753
@model_validator(mode="after")
3854
def _validate_obj(self) -> Self:
55+
if not self.metadata:
56+
return self
3957

4058
if not self.metadata.input_fields or len(self.metadata.input_fields) == 0:
4159
raise ValueError("input_fields not found in the metadata")
@@ -70,38 +88,12 @@ def _validate_obj(self) -> Self:
7088

7189
return self
7290

73-
def _get_io_types(self, data: Any) -> Tuple[Schema, Schema]:
74-
data_schema = None
75-
if isinstance(data, BaseModel):
76-
data_schema = data.model_json_schema()
77-
# If input schema is provided it overwrites the data schema
78-
input_schema = (
79-
self.metadata.input_schema if self.metadata.input_schema else data_schema
80-
)
81-
# If output schema is provided it overwrites the data schema
82-
output_schema = (
83-
self.metadata.output_schema if self.metadata.output_schema else data_schema
84-
)
85-
86-
if not input_schema or not output_schema:
87-
raise ValueError(
88-
"input_schema, and or output_schema are missing from the metadata, for a better accuracy you are required to provide them in this scenario, or we could not infer the type from the state"
89-
)
90-
91-
input_type = Schema.model_validate(
92-
create_type_from_schema(input_schema, self.metadata.input_fields)
93-
)
94-
95-
output_type = Schema.model_validate(
96-
create_type_from_schema(output_schema, self.metadata.output_fields)
97-
)
98-
99-
return (input_type, output_type)
100-
10191
def langgraph_node(self, data: Any, config: Optional[dict] = None) -> Runnable:
92+
"""This method is used to add a language graph node to a langgraph multi-agent software.
93+
It leverages language models for IO mapping, ensuring efficient communication between agents.
94+
"""
10295

103-
# If there is a template for the output the output_schema is going to be ignored in the translation
104-
input_type, output_type = self._get_io_types(data)
96+
input_type, output_type = get_io_types(data, self.metadata)
10597

10698
data_to_be_mapped = extract_nested_fields(
10799
data, fields=self.metadata.input_fields
@@ -132,3 +124,64 @@ def langgraph_node(self, data: Any, config: Optional[dict] = None) -> Runnable:
132124

133125
iomapper_config = LangGraphIOMapperConfig(llm=self.llm)
134126
return LangGraphIOMapper(iomapper_config, input).as_runnable()
127+
128+
def langgraph_imperative(
129+
self, data: Any, config: Optional[dict] = None
130+
) -> Runnable:
131+
"""
132+
Description: Similar to langgraph_node, this method adds a language graph node to a multi-agent software.
133+
However, it does not utilize a language model for IO mapping, offering an imperative approach to agent integration.
134+
"""
135+
136+
input_type, output_type = self._get_io_types(data, self.metadata)
137+
138+
data_to_be_mapped = extract_nested_fields(
139+
data, fields=self.metadata.input_fields
140+
)
141+
142+
input = ImperativeIOMapperInput(
143+
input=ArgumentsDescription(
144+
json_schema=input_type,
145+
),
146+
output=ArgumentsDescription(json_schema=output_type),
147+
data=data_to_be_mapped,
148+
)
149+
150+
if not self.metadata.field_mapping:
151+
raise ValueError(
152+
"In order to use imperative mapping field_mapping must be provided in the metadata"
153+
)
154+
155+
imperative_io_mapper = ImperativeIOMapper(
156+
input=input, field_mapping=self.metadata.field_mapping
157+
)
158+
return imperative_io_mapper.as_runnable()
159+
160+
@staticmethod
161+
def as_worfklow_step(workflow: Workflow) -> Callable:
162+
"""This static method allows for the addition of a step to a LlamaIndex workflow.
163+
It integrates seamlessly into workflows, enabling structured progression and task execution.
164+
"""
165+
io_mapper_step = LLamaIndexIOMapper.llamaindex_mapper(workflow)
166+
return io_mapper_step
167+
168+
@staticmethod
169+
def as_workflow_agent(
170+
mapping_metadata: IOMappingAgentMetadata,
171+
llm: BaseLLM,
172+
name: str,
173+
description: str,
174+
can_handoff_to: Optional[List[str]] = None,
175+
tools: Optional[List[Union[BaseTool, Callable]]] = [],
176+
):
177+
"""This static method returns an instance of an agent that can be integrated into a Multi AgentWorkflow.
178+
It provides robust IO mapping capabilities essential for complex multi agent workflow interactions.
179+
"""
180+
return LLamaIndexIOMapper(
181+
mapping_metadata=mapping_metadata,
182+
llm=llm,
183+
tools=tools,
184+
name=name,
185+
description=description,
186+
can_handoff_to=can_handoff_to,
187+
)

0 commit comments

Comments
 (0)