mirror of
https://github.com/langgenius/dify.git
synced 2026-06-08 09:27:39 +08:00
Co-authored-by: Copilot <copilot@github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
134 lines
4.3 KiB
Python
134 lines
4.3 KiB
Python
"""Pydantic AI bridge example for use from a source checkout.
|
|
|
|
`agenton_examples` is not part of the published package, so run this module from
|
|
the repository root with:
|
|
|
|
PYTHONPATH=dify-agent/src:dify-agent/examples/agenton \
|
|
uv run --project dify-agent python -m agenton_examples.pydantic_ai_bridge
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
import os
|
|
from dataclasses import dataclass
|
|
|
|
from pydantic_ai import Agent, RunContext
|
|
from pydantic_ai.messages import BuiltinToolCallPart, ModelMessage, ToolCallPart
|
|
from pydantic_ai.models.openai import OpenAIChatModel # pyright: ignore[reportDeprecated]
|
|
from pydantic_ai.models.test import TestModel
|
|
|
|
from agenton.compositor import Compositor, LayerNode, LayerProvider
|
|
from agenton_collections.layers.plain import ObjectLayer, PromptLayer, PromptLayerConfig, ToolsLayer
|
|
from agenton_collections.layers.pydantic_ai import PydanticAIBridgeLayer
|
|
from agenton_collections.transformers.pydantic_ai import PYDANTIC_AI_TRANSFORMERS
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class AgentProfile:
|
|
name: str
|
|
audience: str
|
|
tone: str
|
|
|
|
|
|
def count_words(text: str) -> int:
|
|
return len(text.split())
|
|
|
|
|
|
def profile_prompt(ctx: RunContext[AgentProfile]) -> str:
|
|
profile = ctx.deps
|
|
return f"You are {profile.name}, helping {profile.audience}."
|
|
|
|
|
|
def tone_prompt(ctx: RunContext[AgentProfile]) -> str:
|
|
return f"Keep responses {ctx.deps.tone}."
|
|
|
|
|
|
def write_tagline(ctx: RunContext[AgentProfile], topic: str) -> str:
|
|
profile = ctx.deps
|
|
return f"{profile.name}: {topic} for {profile.audience}, in a {profile.tone} voice."
|
|
|
|
|
|
async def main() -> None:
|
|
profile = AgentProfile(
|
|
name="Agenton Assistant",
|
|
audience="engineers composing agent capabilities",
|
|
tone="precise and friendly",
|
|
)
|
|
compositor = Compositor(
|
|
[
|
|
LayerNode("base_prompt", PromptLayer),
|
|
LayerNode(
|
|
"profile",
|
|
LayerProvider.from_factory(
|
|
layer_type=ObjectLayer,
|
|
create=lambda _config: ObjectLayer[AgentProfile](profile),
|
|
),
|
|
),
|
|
LayerNode(
|
|
"plain_tools",
|
|
LayerProvider.from_factory(
|
|
layer_type=ToolsLayer,
|
|
create=lambda _config: ToolsLayer(tool_entries=(count_words,)),
|
|
),
|
|
),
|
|
LayerNode(
|
|
"pydantic_ai_bridge",
|
|
LayerProvider.from_factory(
|
|
layer_type=PydanticAIBridgeLayer,
|
|
create=lambda _config: PydanticAIBridgeLayer[AgentProfile](
|
|
prefix=("Prefer concrete details.", profile_prompt, tone_prompt),
|
|
user="Use the tools for 'layer composition'.",
|
|
tool_entries=(write_tagline,),
|
|
),
|
|
),
|
|
deps={"object_layer": "profile"},
|
|
),
|
|
],
|
|
**PYDANTIC_AI_TRANSFORMERS,
|
|
)
|
|
|
|
async with compositor.enter(
|
|
configs={
|
|
"base_prompt": PromptLayerConfig(
|
|
prefix="Use the available tools before answering.",
|
|
suffix="Return concise, inspectable output.",
|
|
)
|
|
}
|
|
) as run:
|
|
model = (
|
|
OpenAIChatModel("gpt-5.5") # pyright: ignore[reportDeprecated]
|
|
if os.getenv("OPENAI_API_KEY")
|
|
else TestModel()
|
|
)
|
|
agent = Agent[AgentProfile](
|
|
model=model,
|
|
deps_type=AgentProfile,
|
|
tools=run.tools,
|
|
)
|
|
for prompt in run.prompts:
|
|
_ = agent.system_prompt(prompt)
|
|
|
|
bridge_layer = run.get_layer("pydantic_ai_bridge", PydanticAIBridgeLayer)
|
|
result = await agent.run(run.user_prompts, deps=bridge_layer.run_deps)
|
|
|
|
for line in _format_messages(result.all_messages()):
|
|
print(line)
|
|
|
|
|
|
def _format_messages(messages: list[ModelMessage]) -> list[str]:
|
|
lines: list[str] = []
|
|
for message in messages:
|
|
for part in message.parts:
|
|
if isinstance(part, ToolCallPart | BuiltinToolCallPart):
|
|
args = json.dumps(part.args, ensure_ascii=False)
|
|
lines.append(f"{type(part).__name__}: {part.tool_name}({args})")
|
|
else:
|
|
lines.append(f"{type(part).__name__}: {part.content}")
|
|
return lines
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|