-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathagent.py
More file actions
119 lines (97 loc) · 5.22 KB
/
Copy pathagent.py
File metadata and controls
119 lines (97 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
"""
Example use case for BeeAI integration: utilizing a Mellea program to write an email with an IVF loop.
"""
import os
import asyncio
import sys
import inspect
from typing import Annotated, Callable
from a2a.types import Message
from agentstack_sdk.a2a.types import AgentMessage
from agentstack_sdk.server import Server
from agentstack_sdk.a2a.extensions import (
LLMServiceExtensionServer, LLMServiceExtensionSpec,
TrajectoryExtensionServer, TrajectoryExtensionSpec,
AgentDetail
)
from agentstack_sdk.a2a.extensions.ui.form import (
FormExtensionServer, FormExtensionSpec, FormRender, TextField
)
from mellea import MelleaSession, start_session
from mellea.stdlib.base import ChatContext, ModelOutputThunk
from mellea.backends.openai import OpenAIBackend
from mellea.stdlib.sampling import RejectionSamplingStrategy
from mellea.stdlib.sampling.types import SamplingResult
from mellea.stdlib.sampling.base import Context
from mellea.stdlib.requirement import req, Requirement, simple_validate
def bee_app(func: Callable) -> Callable:
"""Serves as a wrapper that takes any Mellea program and converts it to a BeeAI Agent. This is an example for an email writer."""
server = Server()
params : dict = inspect.signature(func).parameters # Mapping params from Mellea function onto form inputs
form_fields : list[str] = list(params.keys())[1:-1]
all_fields : list[TextField] = []
for field in form_fields:
all_fields.append(TextField(id=field, label=field, col_span=2)) #Maps all input params from Mellea agent into BeeAI Forms
form_render = FormRender(
id="input_form",
title="Please provide your information",
columns=2,
fields=all_fields
)
form_extension_spec = FormExtensionSpec(form_render)
#@server.agent(name="Mellea Agent", detail=AgentDetail(interaction_mode="single-turn", author={"name": "Mellea Team"}, source_code_url="https://github.com/supriyalall/mellea/tree/main"), description="BeeAI Agent with Mellea backend")
@server.agent()
async def mellea_agent(input: Message,
llm: Annotated[LLMServiceExtensionServer, LLMServiceExtensionSpec.single_demand()],
trajectory: Annotated[TrajectoryExtensionServer, TrajectoryExtensionSpec()],
form: Annotated[FormExtensionServer,
form_extension_spec]):
"""BeeAI Agent with Mellea Backend -- Email Writer Example"""
form_data = form.parse_form_response(message=input)
inputs = [form_data.values[key].value for key in form_data.values] # Extracting all of the user inputs from the form
llm_config = llm.data.llm_fulfillments.get("default")
for i in range(2): #Fixed loop budget to two iterations
yield trajectory.trajectory_metadata(title=f"Attempt {i + 1}/2", content=f"Generating message...")
m = MelleaSession(OpenAIBackend(
model_id=llm_config.api_model,
api_key=llm_config.api_key,
base_url=llm_config.api_base
))
sampling = await asyncio.to_thread(func, m, *inputs)
validations = sampling.sample_validations[0]
all_passed = all(bool(val_result) for _, val_result in validations)
if all_passed:
yield trajectory.trajectory_metadata(title=f"✓ Attempt {i + 1} succeeded!")
yield AgentMessage(text=sampling.value)
return
status = "\n".join(f"{'✓' if bool(v) else '✗'} {getattr(r, 'description', str(r))}" for r, v in validations)
yield trajectory.trajectory_metadata(title=f"✗ Attempt {i + 1} failed", content=status)
yield trajectory.trajectory_metadata(title=f"All sampling failed. Returning last result.")
yield AgentMessage(text=sampling.value)
server.run(host=os.getenv("HOST", "127.0.0.1"), port=int(os.getenv("PORT", 8000)))
return wrapper
def run():
# Mellea program shown below!
@bee_app
def melleaß_func(m: MelleaSession, sender: str, recipient, subject: str, topic: str, sampling_iters : int = 3) -> tuple[ModelOutputThunk, Context] | SamplingResult:
"""
Example email writing module that utilizes an IVR loop in Mellea to generate an email with a specific list of requirements.
Inputs:
sender: str
recipient: str
subject: str
topic: str
Output:
sampling: tuple[ModelOutputThunk, Context] | SamplingResult
"""
requirements = [
req("Be formal."),
req("Be funny."),
req(f"Make sure that the email is from {sender}, is towards {recipient}, has {subject} as the subject, and is focused on {topic} as a topic"),
Requirement("Use less than 100 words.",
validation_fn=simple_validate(lambda o: len(o.split()) < 100))
]
sampling = m.instruct(f"Write an email from {sender}. Subject of email is {subject}. Name of recipient is {recipient}. Topic of email should be {topic}.", requirements=requirements, strategy=RejectionSamplingStrategy(loop_budget=1), return_sampling_results=True)
return sampling
if __name__ == "__main__":
run()