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
4 changes: 3 additions & 1 deletion .github/workflows/publish-tag-to-pypi.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Publish Tag to PyPI

on: push
on:
push:
workflow_dispatch:

jobs:
build:
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "0.2.2"
description = "Volcengine agent development kit, integrations with Volcengine cloud services."
readme = "README.md"
requires-python = ">=3.10"
license = { file = "LICENSE" }
authors = [
{name = "Yaozheng Fang", email = "fangyozheng@gmail.com"},
{name = "Guodong Li", email = "cu.eric.lee@gmail.com"},
Expand All @@ -21,7 +22,7 @@ dependencies = [
"opentelemetry-exporter-otlp>=1.35.0",
"opentelemetry-instrumentation-logging>=0.56b0",
"wrapt>=1.17.2", # For patching built-in functions
"openai==1.99.9" # For fix https://github.com/BerriAI/litellm/issues/13710
"openai<1.100" # For fix https://github.com/BerriAI/litellm/issues/13710
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

openai>=1.99.1

]

[project.scripts]
Expand Down
3 changes: 1 addition & 2 deletions veadk/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ class Agent(LlmAgent):
instruction: str = DEFAULT_INSTRUCTION
"""The instruction for the agent, such as principles of function calling."""

# factory
model_name: str = getenv("MODEL_AGENT_NAME", DEFAULT_MODEL_AGENT_NAME)
"""The name of the model for agent running."""

Expand Down Expand Up @@ -122,7 +121,7 @@ def model_post_init(self, __context: Any) -> None:
self.after_model_callback.append(tracer.tracer_hook_after_model)
self.after_tool_callback.append(tracer.tracer_hook_after_tool)

logger.info(f"Agent `{self.name}` init done.")
logger.info(f"{self.__class__.__name__} `{self.name}` init done.")
logger.debug(
f"Agent: {self.model_dump(include={'name', 'model_name', 'model_api_base', 'tools', 'serve_url'})}"
)
Expand Down
70 changes: 70 additions & 0 deletions veadk/agents/loop_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from google.adk.agents import LoopAgent as GoogleADKLoopAgent
from google.adk.agents.base_agent import BaseAgent
from pydantic import ConfigDict, Field
from typing_extensions import Any

from veadk.agent import Agent
from veadk.prompts.agent_default_prompt import DEFAULT_DESCRIPTION, DEFAULT_INSTRUCTION
from veadk.tracing.base_tracer import BaseTracer
from veadk.utils.logger import get_logger
from veadk.utils.patches import patch_asyncio

patch_asyncio()
logger = get_logger(__name__)


class LoopAgent(GoogleADKLoopAgent):
"""Loop Agent with Volcengine capabilities."""

model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
"""The model config"""

name: str = "veLoopAgent"
"""The name of the agent."""

description: str = DEFAULT_DESCRIPTION
"""The description of the agent. This will be helpful in A2A scenario."""

instruction: str = DEFAULT_INSTRUCTION
"""The instruction for the agent, such as principles of function calling."""

sub_agents: list[BaseAgent] = Field(default_factory=list, exclude=True)
"""The sub agents provided to agent."""

tracers: list[BaseTracer] = []
"""The tracers provided to agent."""

def set_sub_agents_tracer(self, tracer) -> None:
from veadk.agents.parallel_agent import ParallelAgent
from veadk.agents.sequential_agent import SequentialAgent

for sub_agent in self.sub_agents:
if isinstance(sub_agent, Agent):
tracer.do_hooks(sub_agent)
elif isinstance(sub_agent, (SequentialAgent, LoopAgent, ParallelAgent)):
sub_agent.set_sub_agents_tracer(tracer)

def model_post_init(self, __context: Any) -> None:
super().model_post_init(None) # for sub_agents init

if self.tracers:
for tracer in self.tracers:
self.set_sub_agents_tracer(tracer)

logger.info(f"{self.__class__.__name__} `{self.name}` init done.")
73 changes: 73 additions & 0 deletions veadk/agents/parallel_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from google.adk.agents import ParallelAgent as GoogleADKParallelAgent
from google.adk.agents.base_agent import BaseAgent
from pydantic import ConfigDict, Field
from typing_extensions import Any

from veadk.agent import Agent
from veadk.prompts.agent_default_prompt import DEFAULT_DESCRIPTION, DEFAULT_INSTRUCTION
from veadk.tracing.base_tracer import BaseTracer
from veadk.utils.logger import get_logger
from veadk.utils.patches import patch_asyncio

patch_asyncio()
logger = get_logger(__name__)


class ParallelAgent(GoogleADKParallelAgent):
"""LLM-based Agent with Volcengine capabilities."""

model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
"""The model config"""

name: str = "veParallelAgent"
"""The name of the agent."""

description: str = DEFAULT_DESCRIPTION
"""The description of the agent. This will be helpful in A2A scenario."""

instruction: str = DEFAULT_INSTRUCTION
"""The instruction for the agent, such as principles of function calling."""

sub_agents: list[BaseAgent] = Field(default_factory=list, exclude=True)
"""The sub agents provided to agent."""

tracers: list[BaseTracer] = []
"""The tracers provided to agent."""

def set_sub_agents_tracer(self, tracer) -> None:
from veadk.agents.loop_agent import LoopAgent
from veadk.agents.sequential_agent import SequentialAgent

for sub_agent in self.sub_agents:
if isinstance(sub_agent, Agent):
tracer.do_hooks(sub_agent)
elif isinstance(sub_agent, (SequentialAgent, LoopAgent, ParallelAgent)):
sub_agent.set_sub_agents_tracer(tracer)

def model_post_init(self, __context: Any) -> None:
super().model_post_init(None) # for sub_agents init

if self.tracers:
logger.warning(
"Enable tracing in ParallelAgent may cause OpenTelemetry context error. Issue see https://github.com/google/adk-python/issues/1670"
)
for tracer in self.tracers:
self.set_sub_agents_tracer(tracer)

logger.info(f"{self.__class__.__name__} `{self.name}` init done.")
70 changes: 70 additions & 0 deletions veadk/agents/sequential_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from google.adk.agents import SequentialAgent as GoogleADKSequentialAgent
from google.adk.agents.base_agent import BaseAgent
from pydantic import ConfigDict, Field
from typing_extensions import Any

from veadk.agent import Agent
from veadk.prompts.agent_default_prompt import DEFAULT_DESCRIPTION, DEFAULT_INSTRUCTION
from veadk.tracing.base_tracer import BaseTracer
from veadk.utils.logger import get_logger
from veadk.utils.patches import patch_asyncio

patch_asyncio()
logger = get_logger(__name__)


class SequentialAgent(GoogleADKSequentialAgent):
"""Sequential Agent with Volcengine capabilities."""

model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
"""The model config"""

name: str = "veSequentialAgent"
"""The name of the agent."""

description: str = DEFAULT_DESCRIPTION
"""The description of the agent. This will be helpful in A2A scenario."""

instruction: str = DEFAULT_INSTRUCTION
"""The instruction for the agent, such as principles of function calling."""

sub_agents: list[BaseAgent] = Field(default_factory=list, exclude=True)
"""The sub agents provided to agent."""

tracers: list[BaseTracer] = []
"""The tracers provided to agent."""

def set_sub_agents_tracer(self, tracer) -> None:
from veadk.agents.loop_agent import LoopAgent
from veadk.agents.parallel_agent import ParallelAgent

for sub_agent in self.sub_agents:
if isinstance(sub_agent, Agent):
tracer.do_hooks(sub_agent)
elif isinstance(sub_agent, (SequentialAgent, LoopAgent, ParallelAgent)):
sub_agent.set_sub_agents_tracer(tracer)

def model_post_init(self, __context: Any) -> None:
super().model_post_init(None) # for sub_agents init

if self.tracers:
for tracer in self.tracers:
self.set_sub_agents_tracer(tracer)

logger.info(f"{self.__class__.__name__} `{self.name}` init done.")
13 changes: 10 additions & 3 deletions veadk/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

from veadk.a2a.remote_ve_agent import RemoteVeAgent
from veadk.agent import Agent
from veadk.agents.loop_agent import LoopAgent
from veadk.agents.parallel_agent import ParallelAgent
from veadk.agents.sequential_agent import SequentialAgent
from veadk.evaluation import EvalSetRecorder
from veadk.memory.short_term_memory import ShortTermMemory
from veadk.types import MediaMessage
Expand All @@ -38,11 +41,13 @@
list[MediaMessage | str], # multiple turn prompt with media and text-based prompt
]

VeAgent = Union[Agent, RemoteVeAgent, SequentialAgent, ParallelAgent, LoopAgent]


class Runner:
def __init__(
self,
agent: Agent | RemoteVeAgent,
agent: VeAgent,
short_term_memory: ShortTermMemory,
app_name: str = "veadk_default_app",
user_id: str = "veadk_default_user",
Expand Down Expand Up @@ -166,10 +171,12 @@ async def run(
return final_output

def save_tracing_file(self, session_id: str) -> str:
if not isinstance(self.agent, Agent):
if not isinstance(
self.agent, (Agent, SequentialAgent, ParallelAgent, LoopAgent)
):
logger.warning(
(
"The agent is not an instance of VeADK Agent, cannot save tracing file."
"The agent is not an instance of Agent, SequentialAgent, ParallelAgent or LoopAgent, cannot save tracing file."
)
)
return ""
Expand Down
7 changes: 6 additions & 1 deletion veadk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
from pydantic import BaseModel, Field

from veadk.agent import Agent
from veadk.agents.loop_agent import LoopAgent
from veadk.agents.parallel_agent import ParallelAgent
from veadk.agents.sequential_agent import SequentialAgent
from veadk.memory.short_term_memory import ShortTermMemory


Expand All @@ -35,7 +38,9 @@ class AgentRunConfig(BaseModel):
default="veadk_vefaas_app", description="The name of the application"
)

agent: Agent = Field(..., description="The root agent instance")
agent: Agent | SequentialAgent | ParallelAgent | LoopAgent = Field(
..., description="The root agent instance"
)

short_term_memory: ShortTermMemory = Field(
default_factory=ShortTermMemory, description="The short-term memory instance"
Expand Down