Skip to content

Commit 3970438

Browse files
evening changes
1 parent aa7c000 commit 3970438

6 files changed

Lines changed: 166 additions & 104 deletions

File tree

src/processor/src/libs/agent_framework/agent_builder.py

Lines changed: 99 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
AgentMiddleware,
1212
BaseChatClient,
1313
ChatMiddleware,
14+
ChatOptions,
1415
ContextProvider,
1516
FunctionTool,
1617
ToolMode,
@@ -441,32 +442,61 @@ def build(self) -> Agent:
441442
async with agent:
442443
response = await agent.run("Hello!")
443444
"""
445+
# Build default_options from model parameters
446+
options_dict: dict[str, Any] = {}
447+
if self._frequency_penalty is not None:
448+
options_dict["frequency_penalty"] = self._frequency_penalty
449+
if self._logit_bias is not None:
450+
options_dict["logit_bias"] = self._logit_bias
451+
if self._max_tokens is not None:
452+
options_dict["max_tokens"] = self._max_tokens
453+
if self._metadata is not None:
454+
options_dict["metadata"] = self._metadata
455+
if self._model_id is not None:
456+
options_dict["model"] = self._model_id
457+
if self._presence_penalty is not None:
458+
options_dict["presence_penalty"] = self._presence_penalty
459+
if self._response_format is not None:
460+
options_dict["response_format"] = self._response_format
461+
if self._seed is not None:
462+
options_dict["seed"] = self._seed
463+
if self._stop is not None:
464+
options_dict["stop"] = self._stop
465+
if self._store is not None:
466+
options_dict["store"] = self._store
467+
if self._temperature is not None:
468+
options_dict["temperature"] = self._temperature
469+
if self._tool_choice is not None:
470+
options_dict["tool_choice"] = self._tool_choice
471+
if self._top_p is not None:
472+
options_dict["top_p"] = self._top_p
473+
if self._user is not None:
474+
options_dict["user"] = self._user
475+
if self._additional_chat_options:
476+
options_dict.update(self._additional_chat_options)
477+
478+
default_options = ChatOptions(**options_dict) if options_dict else None
479+
480+
# Agent expects context_providers as a Sequence; wrap single instance in a list
481+
ctx_providers = self._context_providers
482+
if ctx_providers is not None and not isinstance(ctx_providers, list):
483+
ctx_providers = [ctx_providers]
484+
485+
# Agent expects middleware as a Sequence; wrap single instance in a list
486+
mw = self._middleware
487+
if mw is not None and not isinstance(mw, list):
488+
mw = [mw]
489+
444490
return Agent(
445-
chat_client=self._chat_client,
491+
self._chat_client,
446492
instructions=self._instructions,
447493
id=self._id,
448494
name=self._name,
449495
description=self._description,
450-
chat_message_store_factory=self._chat_message_store_factory,
451-
conversation_id=self._conversation_id,
452-
context_providers=self._context_providers,
453-
middleware=self._middleware,
454-
frequency_penalty=self._frequency_penalty,
455-
logit_bias=self._logit_bias,
456-
max_tokens=self._max_tokens,
457-
metadata=self._metadata,
458-
model_id=self._model_id,
459-
presence_penalty=self._presence_penalty,
460-
response_format=self._response_format,
461-
seed=self._seed,
462-
stop=self._stop,
463-
store=self._store,
464-
temperature=self._temperature,
465-
tool_choice=self._tool_choice,
466496
tools=self._tools,
467-
top_p=self._top_p,
468-
user=self._user,
469-
additional_chat_options=self._additional_chat_options,
497+
default_options=default_options,
498+
context_providers=ctx_providers,
499+
middleware=mw,
470500
**self._kwargs,
471501
)
472502

@@ -755,31 +785,60 @@ def create_agent(
755785
``async with`` to ensure proper initialization and cleanup via the Agent's
756786
async context manager protocol.
757787
"""
788+
# Build default_options from model parameters
789+
opts: dict[str, Any] = {}
790+
if frequency_penalty is not None:
791+
opts["frequency_penalty"] = frequency_penalty
792+
if logit_bias is not None:
793+
opts["logit_bias"] = logit_bias
794+
if max_tokens is not None:
795+
opts["max_tokens"] = max_tokens
796+
if metadata is not None:
797+
opts["metadata"] = metadata
798+
if model_id is not None:
799+
opts["model"] = model_id
800+
if presence_penalty is not None:
801+
opts["presence_penalty"] = presence_penalty
802+
if response_format is not None:
803+
opts["response_format"] = response_format
804+
if seed is not None:
805+
opts["seed"] = seed
806+
if stop is not None:
807+
opts["stop"] = stop
808+
if store is not None:
809+
opts["store"] = store
810+
if temperature is not None:
811+
opts["temperature"] = temperature
812+
if tool_choice is not None:
813+
opts["tool_choice"] = tool_choice
814+
if top_p is not None:
815+
opts["top_p"] = top_p
816+
if user is not None:
817+
opts["user"] = user
818+
if additional_chat_options:
819+
opts.update(additional_chat_options)
820+
821+
default_options = ChatOptions(**opts) if opts else None
822+
823+
# Agent expects context_providers as a Sequence; wrap single instance in a list
824+
ctx_providers = context_providers
825+
if ctx_providers is not None and not isinstance(ctx_providers, list):
826+
ctx_providers = [ctx_providers]
827+
828+
# Agent expects middleware as a Sequence; wrap single instance in a list
829+
mw = middleware
830+
if mw is not None and not isinstance(mw, list):
831+
mw = [mw]
832+
758833
return Agent(
759-
chat_client=chat_client,
834+
chat_client,
760835
instructions=instructions,
761836
id=id,
762837
name=name,
763838
description=description,
764-
chat_message_store_factory=chat_message_store_factory,
765-
conversation_id=conversation_id,
766-
context_providers=context_providers,
767-
middleware=middleware,
768-
frequency_penalty=frequency_penalty,
769-
logit_bias=logit_bias,
770-
max_tokens=max_tokens,
771-
metadata=metadata,
772-
model_id=model_id,
773-
presence_penalty=presence_penalty,
774-
response_format=response_format,
775-
seed=seed,
776-
stop=stop,
777-
store=store,
778-
temperature=temperature,
779-
tool_choice=tool_choice,
780839
tools=tools,
781-
top_p=top_p,
782-
user=user,
783-
additional_chat_options=additional_chat_options,
840+
default_options=default_options,
841+
context_providers=ctx_providers,
842+
middleware=mw,
784843
**kwargs,
785844
)

src/processor/src/libs/agent_framework/agent_info.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55

66
from typing import Any, Callable, MutableMapping, Sequence
77

8-
from agent_framework import FunctionTool
8+
from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool
99
from jinja2 import Template
1010
from openai import BaseModel
1111
from pydantic import Field
1212

1313
from .agent_framework_helper import AgentFrameworkHelper, ClientType
1414

15+
ToolType = FunctionTool | MCPStreamableHTTPTool | MCPStdioTool | Callable[..., Any] | MutableMapping[str, Any]
16+
1517

1618
class AgentInfo(BaseModel):
1719
agent_name: str
@@ -21,10 +23,8 @@ class AgentInfo(BaseModel):
2123
agent_instruction: str | None = Field(default=None)
2224
agent_framework_helper: AgentFrameworkHelper | None = Field(default=None)
2325
tools: (
24-
FunctionTool
25-
| Callable[..., Any]
26-
| MutableMapping[str, Any]
27-
| Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]]
26+
ToolType
27+
| Sequence[ToolType]
2828
| None
2929
) = Field(default=None)
3030

src/processor/src/libs/agent_framework/azure_openai_response_retry.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,27 @@ def __init__(
539539
# Map legacy params to OpenAIChatClient params
540540
if deployment_name and "model" not in kwargs:
541541
kwargs["model"] = deployment_name
542-
if endpoint and "azure_endpoint" not in kwargs:
542+
if endpoint and not kwargs.get("azure_endpoint"):
543543
kwargs["azure_endpoint"] = endpoint
544544
if ad_token_provider and kwargs.get("credential") is None:
545545
kwargs["credential"] = ad_token_provider
546546

547+
# Remove None-valued keys that would conflict with env-based settings
548+
for k in list(kwargs):
549+
if kwargs[k] is None:
550+
del kwargs[k]
551+
547552
super().__init__(*args, **kwargs)
553+
554+
# OpenAIChatClient appends /v1/ to azure_endpoint but Azure AI Foundry
555+
# endpoints expect /openai/responses (without /v1/). Fix the base URL.
556+
if hasattr(self, "client") and self.client is not None:
557+
base = str(self.client.base_url)
558+
if "/openai/v1/" in base:
559+
import httpx
560+
corrected = base.replace("/openai/v1/", "/openai/")
561+
self.client._base_url = httpx.URL(corrected)
562+
548563
self._retry_config = retry_config or RateLimitRetryConfig.from_env()
549564
self._context_trim_config = ContextTrimConfig.from_env()
550565

src/processor/src/libs/agent_framework/groupchat_orchestrator.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
Role,
2929
SupportsAgentRun,
3030
Workflow,
31-
WorkflowBuilder as GroupChatBuilder,
3231
WorkflowEvent,
3332
)
33+
from agent_framework_orchestrations import GroupChatBuilder
3434
from mem0 import AsyncMemory
3535
from pydantic import BaseModel, ValidationError
3636

@@ -491,7 +491,7 @@ async def run_stream(
491491
# Execute with streaming
492492
conversation: list[Message] = []
493493

494-
async for event in group_chat_workflow.run_stream(task_prompt):
494+
async for event in group_chat_workflow.run(task_prompt, stream=True):
495495
# Enforce wall-clock timeout if configured.
496496
if self.max_seconds is not None:
497497
elapsed = (datetime.now() - start_time).total_seconds()
@@ -1114,9 +1114,10 @@ async def _build_groupchat(self) -> Workflow:
11141114
]
11151115

11161116
return (
1117-
GroupChatBuilder()
1118-
.set_manager(coordinator)
1119-
.participants(participants)
1117+
GroupChatBuilder(
1118+
participants=participants,
1119+
orchestrator_agent=coordinator,
1120+
)
11201121
.build()
11211122
)
11221123

src/processor/src/libs/agent_framework/shared_memory_context_provider.py

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def __init__(
7777
top_k: Number of relevant memories to retrieve per turn.
7878
score_threshold: Minimum similarity score for memory retrieval.
7979
"""
80+
super().__init__(source_id=f"shared_memory_{agent_name}_{step}")
8081
self._memory_store = memory_store
8182
self._agent_name = agent_name
8283
self._step = step
@@ -96,24 +97,28 @@ def __init__(
9697
break
9798
self._prior_steps = _STEP_ORDER[:step_idx] if step_idx else []
9899

99-
async def invoking(
100+
async def before_run(
100101
self,
101-
messages: Message | MutableSequence[Message],
102-
**kwargs,
103-
) -> Context:
102+
*,
103+
agent,
104+
session,
105+
context,
106+
state,
107+
) -> None:
104108
"""Called before the agent's LLM call. Injects relevant shared memories.
105109
106110
Only searches memories from PREVIOUS steps. Within the current step,
107111
agents already see all messages via GroupChat broadcast.
108112
"""
109113
# Skip if this is the first step (no prior memories exist)
110114
if not self._prior_steps:
111-
return Context()
115+
return
112116

113-
# Extract query from the most recent messages
117+
# Extract query from the most recent messages in context
118+
messages = context.get_messages()
114119
query = self._extract_query(messages)
115120
if not query:
116-
return Context()
121+
return
117122

118123
try:
119124
memories = await self._memory_store.search(
@@ -127,15 +132,15 @@ async def invoking(
127132
self._agent_name,
128133
e,
129134
)
130-
return Context()
135+
return
131136

132137
if not memories:
133-
return Context()
138+
return
134139

135140
# Format memories into context instructions
136141
formatted = self._format_memories(memories)
137142
if not formatted:
138-
return Context()
143+
return
139144

140145
instructions = f"{self.DEFAULT_CONTEXT_PROMPT}\n\n{formatted}"
141146

@@ -147,14 +152,15 @@ async def invoking(
147152
len(instructions),
148153
)
149154

150-
return Context(instructions=instructions)
155+
context.extend_instructions(self.source_id, instructions)
151156

152-
async def invoked(
157+
async def after_run(
153158
self,
154-
request_messages: Message | Sequence[Message],
155-
response_messages: Message | Sequence[Message] | None = None,
156-
invoke_exception: Exception | None = None,
157-
**kwargs,
159+
*,
160+
agent,
161+
session,
162+
context,
163+
state,
158164
) -> None:
159165
"""Called after the agent's LLM response. Buffers the response for storage.
160166
@@ -163,33 +169,26 @@ async def invoked(
163169
This means only the agent's last response per step gets stored,
164170
which is the most complete and useful summary.
165171
"""
166-
if invoke_exception is not None:
167-
logger.debug(
168-
"[MEMORY] invoked() skipped for %s — exception: %s",
169-
self._agent_name,
170-
invoke_exception,
171-
)
172-
return
173-
174-
if response_messages is None:
172+
response = context.response
173+
if response is None:
175174
logger.debug(
176-
"[MEMORY] invoked() skipped for %s — no response_messages",
175+
"[MEMORY] after_run() skipped for %s — no response",
177176
self._agent_name,
178177
)
179178
return
180179

181180
# Extract text from response
182-
content = self._extract_text(response_messages)
181+
content = response.text if hasattr(response, "text") else None
183182
if not content or len(content) < MIN_CONTENT_LENGTH_TO_STORE:
184183
logger.debug(
185-
"[MEMORY] invoked() skipped for %s — content too short (%d chars)",
184+
"[MEMORY] after_run() skipped for %s — content too short (%d chars)",
186185
self._agent_name,
187186
len(content) if content else 0,
188187
)
189188
return
190189

191190
logger.info(
192-
"[MEMORY] invoked() buffering for %s (step=%s, %d chars)",
191+
"[MEMORY] after_run() buffering for %s (step=%s, %d chars)",
193192
self._agent_name,
194193
self._step,
195194
len(content),

0 commit comments

Comments
 (0)