feat(agent-runner): enrich plugin runner host context

This commit is contained in:
huanghuoguoguo
2026-05-17 23:26:52 +08:00
parent 19557c3227
commit 036affe01f
9 changed files with 806 additions and 114 deletions

View File

@@ -122,6 +122,7 @@ class AgentRunContextV1(typing.TypedDict):
actor: dict[str, typing.Any] | None
subject: dict[str, typing.Any] | None
messages: list[dict[str, typing.Any]]
prompt: list[dict[str, typing.Any]]
input: AgentInput
params: dict[str, typing.Any]
resources: AgentResources
@@ -221,6 +222,9 @@ class AgentRunContextBuilder:
descriptor.id,
)
streaming_supported = await self._is_stream_output_supported(query)
remove_think = query.pipeline_config.get('output', {}).get('misc', {}).get('remove-think', False)
# Build runtime context
runtime: AgentRuntimeContext = {
'langbot_version': self.ap.ver_mgr.get_current_version(),
@@ -231,6 +235,8 @@ class AgentRunContextBuilder:
'metadata': {
'bot_name': query.variables.get('_monitoring_bot_name', 'Unknown'),
'pipeline_name': query.variables.get('_monitoring_pipeline_name', 'Unknown'),
'streaming_supported': streaming_supported,
'remove_think': remove_think,
},
}
@@ -243,6 +249,7 @@ class AgentRunContextBuilder:
'actor': self._build_actor(query),
'subject': self._build_subject(query),
'messages': messages,
'prompt': self._build_prompt(query),
'input': input,
'params': params,
'resources': resources,
@@ -256,6 +263,7 @@ class AgentRunContextBuilder:
def _build_input(self, query: pipeline_query.Query) -> AgentInput:
"""Build AgentInput from query."""
text = None
text_parts: list[str] = []
contents: list[dict[str, typing.Any]] = []
if query.user_message:
@@ -264,12 +272,17 @@ class AgentRunContextBuilder:
for elem in query.user_message.content:
contents.append(elem.model_dump(mode='json'))
if elem.type == 'text':
text = getattr(elem, 'text', None)
elem_text = getattr(elem, 'text', None)
if elem_text:
text_parts.append(elem_text)
else:
# Single string content
text = str(query.user_message.content)
contents.append({'type': 'text', 'text': text})
if text_parts:
text = ''.join(text_parts)
# Include message_chain for platform-specific format
message_chain_dict = None
if query.message_chain:
@@ -473,6 +486,29 @@ class AgentRunContextBuilder:
return int(time.time() + timeout_seconds)
async def _is_stream_output_supported(self, query: pipeline_query.Query) -> bool:
"""Check whether the current adapter can consume streaming chunks."""
try:
return await query.adapter.is_stream_output_supported()
except AttributeError:
return False
except Exception:
return False
def _build_prompt(self, query: pipeline_query.Query) -> list[dict[str, typing.Any]]:
"""Build effective prompt messages from query.prompt after preprocessing."""
prompt_messages: list[dict[str, typing.Any]] = []
prompt = getattr(query, 'prompt', None)
messages = getattr(prompt, 'messages', None)
if not messages:
return prompt_messages
for msg in messages:
prompt_messages.append(msg.model_dump(mode='json'))
return prompt_messages
def _build_messages(self, query: pipeline_query.Query) -> list[dict[str, typing.Any]]:
"""Build messages list from query."""
messages: list[dict[str, typing.Any]] = []

View File

@@ -71,7 +71,7 @@ class AgentResourceBuilder:
# Build each resource category in parallel
models, tools, knowledge_bases = await asyncio.gather(
self._build_models(manifest_perms, query),
self._build_models(manifest_perms, runner_config, descriptor, query),
self._build_tools(manifest_perms, bound_plugins, bound_mcp_servers, query),
self._build_knowledge_bases(manifest_perms, runner_config, query),
)
@@ -89,10 +89,13 @@ class AgentResourceBuilder:
async def _build_models(
self,
manifest_perms: dict[str, list[str]],
runner_config: dict[str, typing.Any],
descriptor: AgentRunnerDescriptor,
query: typing.Any,
) -> list[ModelResource]:
"""Build models list with SDK v1 field names."""
models: list[ModelResource] = []
seen_model_ids: set[str] = set()
# Check manifest permission
model_perms = manifest_perms.get('models', [])
@@ -101,8 +104,72 @@ class AgentResourceBuilder:
# Get model from query (preproc already resolved this)
model_uuid = getattr(query, 'use_llm_model_uuid', None)
if not model_uuid:
return models
if model_uuid:
await self._append_llm_model_resource(models, seen_model_ids, model_uuid)
# Add fallback models if present
fallback_uuids = query.variables.get('_fallback_model_uuids', [])
for fb_uuid in fallback_uuids:
await self._append_llm_model_resource(models, seen_model_ids, fb_uuid)
# Add model resources referenced by the runner binding config schema.
# This makes authorization generic for AgentRunner plugins instead of
# hard-coding only local-agent's primary/fallback model path.
await self._append_config_declared_model_resources(
models=models,
seen_model_ids=seen_model_ids,
descriptor=descriptor,
runner_config=runner_config,
)
return models
async def _append_config_declared_model_resources(
self,
models: list[ModelResource],
seen_model_ids: set[str],
descriptor: AgentRunnerDescriptor,
runner_config: dict[str, typing.Any],
) -> None:
"""Authorize model-like values selected through DynamicForm fields."""
for item in descriptor.config_schema or []:
if not isinstance(item, dict):
continue
field_name = item.get('name')
field_type = item.get('type')
if not field_name or field_name not in runner_config:
continue
value = runner_config.get(field_name)
if field_type == 'model-fallback-selector':
if isinstance(value, str):
await self._append_llm_model_resource(models, seen_model_ids, value)
elif isinstance(value, dict):
primary = value.get('primary')
if isinstance(primary, str):
await self._append_llm_model_resource(models, seen_model_ids, primary)
fallbacks = value.get('fallbacks', [])
if isinstance(fallbacks, list):
for fallback_uuid in fallbacks:
if isinstance(fallback_uuid, str):
await self._append_llm_model_resource(models, seen_model_ids, fallback_uuid)
elif field_type == 'llm-model-selector':
if isinstance(value, str):
await self._append_llm_model_resource(models, seen_model_ids, value)
elif field_type == 'rerank-model-selector':
if isinstance(value, str):
await self._append_rerank_model_resource(models, seen_model_ids, value)
async def _append_llm_model_resource(
self,
models: list[ModelResource],
seen_model_ids: set[str],
model_uuid: str | None,
) -> None:
"""Append an LLM model resource if it exists and has not been added."""
if not model_uuid or model_uuid == '__none__' or model_uuid in seen_model_ids:
return
try:
model = await self.ap.model_mgr.get_model_by_uuid(model_uuid)
@@ -112,24 +179,31 @@ class AgentResourceBuilder:
'model_type': getattr(model.model_entity, 'model_type', None),
'provider': getattr(model.provider_entity, 'name', None) if hasattr(model, 'provider_entity') else None,
})
seen_model_ids.add(model_uuid)
except Exception as e:
self.ap.logger.warning(f'Failed to build model resource {model_uuid}: {e}')
self.ap.logger.warning(f'Failed to build LLM model resource {model_uuid}: {e}')
# Add fallback models if present
fallback_uuids = query.variables.get('_fallback_model_uuids', [])
for fb_uuid in fallback_uuids:
try:
model = await self.ap.model_mgr.get_model_by_uuid(fb_uuid)
if model and model.model_entity:
models.append({
'model_id': fb_uuid,
'model_type': model.model_entity.model_type,
'provider': model.provider_entity.name if hasattr(model, 'provider_entity') else None,
})
except Exception as e:
self.ap.logger.warning(f'Failed to build fallback model resource {fb_uuid}: {e}')
async def _append_rerank_model_resource(
self,
models: list[ModelResource],
seen_model_ids: set[str],
model_uuid: str | None,
) -> None:
"""Append a rerank model resource if it exists and has not been added."""
if not model_uuid or model_uuid == '__none__' or model_uuid in seen_model_ids:
return
return models
try:
model = await self.ap.model_mgr.get_rerank_model_by_uuid(model_uuid)
if model and model.model_entity:
models.append({
'model_id': model_uuid,
'model_type': getattr(model.model_entity, 'model_type', 'rerank') or 'rerank',
'provider': getattr(model.provider_entity, 'name', None) if hasattr(model, 'provider_entity') else None,
})
seen_model_ids.add(model_uuid)
except Exception as e:
self.ap.logger.warning(f'Failed to build rerank model resource {model_uuid}: {e}')
async def _build_tools(
self,