mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-15 23:10:59 +00:00
platfrom
This commit is contained in:
@@ -135,7 +135,8 @@ class BotService:
|
|||||||
binding_type = bot_data.get('binding_type')
|
binding_type = bot_data.get('binding_type')
|
||||||
|
|
||||||
# set use_pipeline_name (for backward compatibility with 'pipeline' binding_type)
|
# set use_pipeline_name (for backward compatibility with 'pipeline' binding_type)
|
||||||
if 'use_pipeline_uuid' in bot_data:
|
# Only validate pipeline when binding_type is 'pipeline' or not set (default to pipeline)
|
||||||
|
if 'use_pipeline_uuid' in bot_data and binding_type != 'workflow':
|
||||||
result = await self.ap.persistence_mgr.execute_async(
|
result = await self.ap.persistence_mgr.execute_async(
|
||||||
sqlalchemy.select(persistence_pipeline.LegacyPipeline).where(
|
sqlalchemy.select(persistence_pipeline.LegacyPipeline).where(
|
||||||
persistence_pipeline.LegacyPipeline.uuid == bot_data['use_pipeline_uuid']
|
persistence_pipeline.LegacyPipeline.uuid == bot_data['use_pipeline_uuid']
|
||||||
@@ -149,7 +150,12 @@ class BotService:
|
|||||||
bot_data['binding_uuid'] = bot_data['use_pipeline_uuid']
|
bot_data['binding_uuid'] = bot_data['use_pipeline_uuid']
|
||||||
bot_data['binding_type'] = 'pipeline'
|
bot_data['binding_type'] = 'pipeline'
|
||||||
else:
|
else:
|
||||||
raise Exception('Pipeline not found')
|
# Only raise error if binding_type is explicitly 'pipeline' or not set
|
||||||
|
if binding_type is None or binding_type == 'pipeline':
|
||||||
|
raise Exception('Pipeline not found')
|
||||||
|
# If binding_type is 'workflow', just clear the use_pipeline_uuid
|
||||||
|
bot_data['use_pipeline_uuid'] = None
|
||||||
|
bot_data['use_pipeline_name'] = None
|
||||||
|
|
||||||
# If binding_uuid is set directly (for workflow), sync use_pipeline_uuid for backward compatibility
|
# If binding_uuid is set directly (for workflow), sync use_pipeline_uuid for backward compatibility
|
||||||
if 'binding_uuid' in bot_data and binding_type == 'workflow':
|
if 'binding_uuid' in bot_data and binding_type == 'workflow':
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
@@ -22,10 +20,13 @@ from ....workflow.entities import (
|
|||||||
Position,
|
Position,
|
||||||
MessageContext,
|
MessageContext,
|
||||||
)
|
)
|
||||||
from langbot_plugin.api.entities.builtin.workflow.enums import ExecutionStatus, NodeStatus
|
from langbot_plugin.api.entities.builtin.workflow.enums import ExecutionStatus, NodeStatus, TriggerType
|
||||||
|
from langbot_plugin.api.entities.builtin.workflow.query import WorkflowQuery
|
||||||
|
from langbot_plugin.api.entities.builtin.provider.session import LauncherTypes
|
||||||
from ....workflow.executor import WorkflowExecutor
|
from ....workflow.executor import WorkflowExecutor
|
||||||
from ....workflow.registry import NodeTypeRegistry
|
from ....workflow.registry import NodeTypeRegistry
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class WorkflowExecutionFailedError(Exception):
|
class WorkflowExecutionFailedError(Exception):
|
||||||
"""Raised when a workflow execution finishes with failed status."""
|
"""Raised when a workflow execution finishes with failed status."""
|
||||||
@@ -399,8 +400,26 @@ class WorkflowService:
|
|||||||
),
|
),
|
||||||
raw_message=message_context_data.get('raw_message', {}),
|
raw_message=message_context_data.get('raw_message', {}),
|
||||||
)
|
)
|
||||||
# Set query from message_content for logging purposes
|
|
||||||
context.query = context.message_context.message_content
|
# Determine launcher_type from is_group flag
|
||||||
|
is_group = message_context_data.get('is_group', False)
|
||||||
|
launcher_type = LauncherTypes.GROUP if is_group else LauncherTypes.PERSON
|
||||||
|
|
||||||
|
# Create WorkflowQuery object with launcher_type for monitoring
|
||||||
|
context.query = WorkflowQuery(
|
||||||
|
workflow_uuid=workflow_uuid,
|
||||||
|
workflow_name=workflow_name,
|
||||||
|
execution_id=execution_uuid,
|
||||||
|
launcher_type=launcher_type,
|
||||||
|
launcher_id=message_context_data.get('sender_id', ''),
|
||||||
|
sender_id=message_context_data.get('sender_id', ''),
|
||||||
|
sender_name=message_context_data.get('sender_name', 'User'),
|
||||||
|
message_context=context.message_context,
|
||||||
|
bot_uuid=bot_id,
|
||||||
|
trigger_type=TriggerType.MESSAGE,
|
||||||
|
trigger_data=raw_trigger_data,
|
||||||
|
variables={},
|
||||||
|
)
|
||||||
|
|
||||||
# Note: Frontend panel logging has been removed.
|
# Note: Frontend panel logging has been removed.
|
||||||
# A new solution will be implemented separately.
|
# A new solution will be implemented separately.
|
||||||
@@ -413,7 +432,9 @@ class WorkflowService:
|
|||||||
|
|
||||||
# Store launcher info for monitoring (used when query is a string)
|
# Store launcher info for monitoring (used when query is a string)
|
||||||
if message_context_data:
|
if message_context_data:
|
||||||
context.variables['_launcher_type'] = 'websocket'
|
# Determine launcher_type from is_group flag
|
||||||
|
is_group = message_context_data.get('is_group', False)
|
||||||
|
context.variables['_launcher_type'] = 'group' if is_group else 'person'
|
||||||
context.variables['_launcher_id'] = message_context_data.get('sender_id', '')
|
context.variables['_launcher_id'] = message_context_data.get('sender_id', '')
|
||||||
context.variables['_sender_name'] = message_context_data.get('sender_name', 'User')
|
context.variables['_sender_name'] = message_context_data.get('sender_name', 'User')
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
from ..core import app, entities as core_entities, taskmgr
|
from ..core import app, entities as core_entities, taskmgr
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from ..discover import engine
|
from ..discover import engine
|
||||||
|
|
||||||
from ..entity.persistence import bot as persistence_bot
|
from ..entity.persistence import bot as persistence_bot
|
||||||
@@ -99,6 +102,74 @@ class RuntimeBot:
|
|||||||
|
|
||||||
return binding_uuid, False
|
return binding_uuid, False
|
||||||
|
|
||||||
|
async def _handle_workflow_message(
|
||||||
|
self,
|
||||||
|
event: platform_events.MessageEvent,
|
||||||
|
adapter: abstract_platform_adapter.AbstractMessagePlatformAdapter,
|
||||||
|
workflow_uuid: str,
|
||||||
|
launcher_type: str,
|
||||||
|
launcher_id: str | int,
|
||||||
|
sender_id: str | int,
|
||||||
|
) -> None:
|
||||||
|
"""Handle message by executing the bound workflow directly."""
|
||||||
|
message_content = str(event.message_chain)
|
||||||
|
message_chain_obj = event.message_chain
|
||||||
|
|
||||||
|
# Build message context
|
||||||
|
sender_name = None
|
||||||
|
if hasattr(event, 'sender'):
|
||||||
|
sender = event.sender
|
||||||
|
if hasattr(sender, 'nickname'):
|
||||||
|
sender_name = sender.nickname
|
||||||
|
elif hasattr(sender, 'member_name'):
|
||||||
|
sender_name = sender.member_name
|
||||||
|
|
||||||
|
is_group = launcher_type == 'group'
|
||||||
|
message_context = {
|
||||||
|
'message_id': str(getattr(event, 'message_id', '')),
|
||||||
|
'message_content': message_content,
|
||||||
|
'sender_id': str(sender_id),
|
||||||
|
'sender_name': sender_name or 'User',
|
||||||
|
'platform': adapter.__class__.__name__,
|
||||||
|
'conversation_id': str(launcher_id),
|
||||||
|
'is_group': is_group,
|
||||||
|
'group_id': str(launcher_id) if is_group else None,
|
||||||
|
'mentions': [],
|
||||||
|
'reply_to': None,
|
||||||
|
'raw_message': {
|
||||||
|
'message': message_chain_obj.model_dump() if hasattr(message_chain_obj, 'model_dump') else str(message_chain_obj),
|
||||||
|
'launcher_id': launcher_id,
|
||||||
|
'session_type': launcher_type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
trigger_data = {
|
||||||
|
'message': message_content,
|
||||||
|
'message_chain': message_chain_obj.model_dump() if hasattr(message_chain_obj, 'model_dump') else str(message_chain_obj),
|
||||||
|
'session_type': launcher_type,
|
||||||
|
'connection_id': str(launcher_id),
|
||||||
|
'message_context': message_context,
|
||||||
|
}
|
||||||
|
|
||||||
|
session_id = f'{launcher_type}_{launcher_id}'
|
||||||
|
logger.info(f'Processing workflow message from {session_id}: {message_content}')
|
||||||
|
|
||||||
|
try:
|
||||||
|
from ..api.http.service.workflow import WorkflowExecutionFailedError
|
||||||
|
|
||||||
|
execution_id = await self.ap.workflow_service.execute_workflow(
|
||||||
|
workflow_uuid=workflow_uuid,
|
||||||
|
trigger_type='message',
|
||||||
|
trigger_data=trigger_data,
|
||||||
|
session_id=session_id,
|
||||||
|
user_id=str(sender_id),
|
||||||
|
bot_id=self.bot_entity.uuid,
|
||||||
|
)
|
||||||
|
except WorkflowExecutionFailedError as e:
|
||||||
|
await self.logger.error(f'Workflow execution failed: {e.message}')
|
||||||
|
except Exception as e:
|
||||||
|
await self.logger.error(f'Workflow execution error: {e}')
|
||||||
|
|
||||||
async def _record_discarded_message(
|
async def _record_discarded_message(
|
||||||
self,
|
self,
|
||||||
launcher_type: provider_session.LauncherTypes,
|
launcher_type: provider_session.LauncherTypes,
|
||||||
@@ -193,6 +264,20 @@ class RuntimeBot:
|
|||||||
|
|
||||||
message_text = str(event.message_chain)
|
message_text = str(event.message_chain)
|
||||||
element_types = [comp.type for comp in event.message_chain]
|
element_types = [comp.type for comp in event.message_chain]
|
||||||
|
binding_type, binding_uuid = self.get_binding_info()
|
||||||
|
|
||||||
|
# Handle workflow binding separately from pipeline
|
||||||
|
if binding_type == 'workflow':
|
||||||
|
await self._handle_workflow_message(
|
||||||
|
event=event,
|
||||||
|
adapter=adapter,
|
||||||
|
workflow_uuid=binding_uuid,
|
||||||
|
launcher_type='person',
|
||||||
|
launcher_id=launcher_id,
|
||||||
|
sender_id=event.sender.id,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
pipeline_uuid, routed_by_rule = self.resolve_pipeline_uuid(
|
pipeline_uuid, routed_by_rule = self.resolve_pipeline_uuid(
|
||||||
'person', launcher_id, message_text, element_types
|
'person', launcher_id, message_text, element_types
|
||||||
)
|
)
|
||||||
@@ -254,6 +339,20 @@ class RuntimeBot:
|
|||||||
|
|
||||||
message_text = str(event.message_chain)
|
message_text = str(event.message_chain)
|
||||||
element_types = [comp.type for comp in event.message_chain]
|
element_types = [comp.type for comp in event.message_chain]
|
||||||
|
binding_type, binding_uuid = self.get_binding_info()
|
||||||
|
|
||||||
|
# Handle workflow binding separately from pipeline
|
||||||
|
if binding_type == 'workflow':
|
||||||
|
await self._handle_workflow_message(
|
||||||
|
event=event,
|
||||||
|
adapter=adapter,
|
||||||
|
workflow_uuid=binding_uuid,
|
||||||
|
launcher_type='group',
|
||||||
|
launcher_id=launcher_id,
|
||||||
|
sender_id=event.sender.id,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
pipeline_uuid, routed_by_rule = self.resolve_pipeline_uuid(
|
pipeline_uuid, routed_by_rule = self.resolve_pipeline_uuid(
|
||||||
'group', launcher_id, message_text, element_types
|
'group', launcher_id, message_text, element_types
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -354,9 +354,19 @@ class RuntimeConnectionHandler(handler.Handler):
|
|||||||
extra_args=extra_args,
|
extra_args=extra_args,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# invoke_llm returns (message, usage_info) tuple
|
||||||
|
if isinstance(result, tuple) and len(result) == 2:
|
||||||
|
msg, usage_info = result
|
||||||
|
msg_dump = msg.model_dump()
|
||||||
|
# Attach usage info to message dump
|
||||||
|
if usage_info:
|
||||||
|
msg_dump['usage'] = usage_info
|
||||||
|
else:
|
||||||
|
msg_dump = result.model_dump()
|
||||||
|
|
||||||
return handler.ActionResponse.success(
|
return handler.ActionResponse.success(
|
||||||
data={
|
data={
|
||||||
'message': result.model_dump(),
|
'message': msg_dump,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ class RuntimeProvider:
|
|||||||
if usage_info:
|
if usage_info:
|
||||||
input_tokens = usage_info.get('input_tokens', 0)
|
input_tokens = usage_info.get('input_tokens', 0)
|
||||||
output_tokens = usage_info.get('output_tokens', 0)
|
output_tokens = usage_info.get('output_tokens', 0)
|
||||||
|
# Attach usage info to message using object.__setattr__ to bypass pydantic validation
|
||||||
|
try:
|
||||||
|
object.__setattr__(msg, 'usage', usage_info)
|
||||||
|
except (AttributeError, TypeError):
|
||||||
|
pass # If we can't set it, just skip it
|
||||||
return msg
|
return msg
|
||||||
else:
|
else:
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -11,19 +11,31 @@ from typing import Any, Optional
|
|||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
# Import SDK entities for standard workflow protocol types
|
# Import SDK entities for standard workflow protocol types
|
||||||
|
# These are re-exported for use by other modules in the workflow package.
|
||||||
from langbot_plugin.api.entities.builtin.workflow.entities import (
|
from langbot_plugin.api.entities.builtin.workflow.entities import (
|
||||||
ExecutionContext,
|
ExecutionContext as ExecutionContext,
|
||||||
ExecutionStep,
|
ExecutionStep as ExecutionStep,
|
||||||
MessageContext,
|
MessageContext as MessageContext,
|
||||||
NodeDefinition,
|
NodeDefinition,
|
||||||
NodeState,
|
NodeState as NodeState,
|
||||||
PortDefinition,
|
PortDefinition as PortDefinition,
|
||||||
)
|
)
|
||||||
from langbot_plugin.api.entities.builtin.workflow.enums import (
|
from langbot_plugin.api.entities.builtin.workflow.enums import (
|
||||||
ExecutionStatus,
|
ExecutionStatus as ExecutionStatus,
|
||||||
NodeStatus,
|
NodeStatus as NodeStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ExecutionContext",
|
||||||
|
"ExecutionStep",
|
||||||
|
"MessageContext",
|
||||||
|
"NodeDefinition",
|
||||||
|
"NodeState",
|
||||||
|
"PortDefinition",
|
||||||
|
"ExecutionStatus",
|
||||||
|
"NodeStatus",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Position(pydantic.BaseModel):
|
class Position(pydantic.BaseModel):
|
||||||
"""Node position on canvas"""
|
"""Node position on canvas"""
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ from .entities import (
|
|||||||
)
|
)
|
||||||
from ..entity.persistence import workflow as persistence_workflow
|
from ..entity.persistence import workflow as persistence_workflow
|
||||||
from .registry import NodeTypeRegistry
|
from .registry import NodeTypeRegistry
|
||||||
from . import monitor
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..core import app
|
from ..core import app
|
||||||
|
|||||||
@@ -20,17 +20,24 @@ import json
|
|||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from ..core import app
|
from ..core import app
|
||||||
from langbot_plugin.api.entities.builtin.workflow.query import WorkflowQuery
|
|
||||||
|
|
||||||
|
|
||||||
class WorkflowMonitoringHelper:
|
class WorkflowMonitoringHelper:
|
||||||
"""Helper class for workflow monitoring operations"""
|
"""Helper class for workflow monitoring operations"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_workflow_query(query) -> bool:
|
||||||
|
"""Check if query is a WorkflowQuery object"""
|
||||||
|
if query is None or isinstance(query, str):
|
||||||
|
return False
|
||||||
|
# Check for WorkflowQuery attributes
|
||||||
|
return hasattr(query, 'launcher_type') or hasattr(query, 'workflow_uuid')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_session_id(query, context_vars: dict | None = None) -> str:
|
def _get_session_id(query, context_vars: dict | None = None) -> str:
|
||||||
"""Build session_id from query or context_vars"""
|
"""Build session_id from query or context_vars"""
|
||||||
# Try to get from query first
|
# Try to get from WorkflowQuery first
|
||||||
if not isinstance(query, str) and query.launcher_type:
|
if WorkflowMonitoringHelper._is_workflow_query(query) and query.launcher_type:
|
||||||
launcher_type = query.launcher_type.value if hasattr(query.launcher_type, 'value') else str(query.launcher_type)
|
launcher_type = query.launcher_type.value if hasattr(query.launcher_type, 'value') else str(query.launcher_type)
|
||||||
launcher_id = query.launcher_id or 'unknown'
|
launcher_id = query.launcher_id or 'unknown'
|
||||||
return f'{launcher_type}_{launcher_id}'
|
return f'{launcher_type}_{launcher_id}'
|
||||||
@@ -44,17 +51,23 @@ class WorkflowMonitoringHelper:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_platform(query, context_vars: dict | None = None) -> str:
|
def _get_platform(query, context_vars: dict | None = None) -> str:
|
||||||
"""Get platform name from query or context_vars"""
|
"""Get platform name from query or context_vars"""
|
||||||
if not isinstance(query, str) and query.launcher_type:
|
# Try WorkflowQuery first
|
||||||
|
if WorkflowMonitoringHelper._is_workflow_query(query) and query.launcher_type:
|
||||||
if hasattr(query.launcher_type, 'value'):
|
if hasattr(query.launcher_type, 'value'):
|
||||||
return query.launcher_type.value
|
return query.launcher_type.value
|
||||||
return str(query.launcher_type)
|
return str(query.launcher_type)
|
||||||
|
|
||||||
|
# Fallback to context_vars for launcher_type (person/group)
|
||||||
|
if context_vars and context_vars.get('_launcher_type'):
|
||||||
|
return context_vars['_launcher_type']
|
||||||
|
|
||||||
return 'workflow'
|
return 'workflow'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_sender_name(query, context_vars: dict | None = None) -> str | None:
|
def _get_sender_name(query, context_vars: dict | None = None) -> str | None:
|
||||||
"""Get sender name from query or context_vars"""
|
"""Get sender name from query or context_vars"""
|
||||||
# Try query first
|
# Try WorkflowQuery first
|
||||||
if not isinstance(query, str):
|
if WorkflowMonitoringHelper._is_workflow_query(query):
|
||||||
if query.sender_name:
|
if query.sender_name:
|
||||||
return query.sender_name
|
return query.sender_name
|
||||||
if query.message_event and hasattr(query.message_event, 'sender'):
|
if query.message_event and hasattr(query.message_event, 'sender'):
|
||||||
|
|||||||
@@ -234,19 +234,25 @@ Respond in the same language as the user's input.
|
|||||||
messages: list,
|
messages: list,
|
||||||
funcs: list | None,
|
funcs: list | None,
|
||||||
extra_args: dict,
|
extra_args: dict,
|
||||||
) -> tuple[Any, Any]:
|
) -> tuple[Any, Any, dict]:
|
||||||
"""Try non-streaming invocation with sequential fallback. Returns (message, model_used)."""
|
"""Try non-streaming invocation with sequential fallback. Returns (message, model_used, usage_info)."""
|
||||||
last_error = None
|
last_error = None
|
||||||
for model in candidates:
|
for model in candidates:
|
||||||
try:
|
try:
|
||||||
msg = await model.provider.invoke_llm(
|
result = await model.provider.invoke_llm(
|
||||||
query=None,
|
query=None,
|
||||||
model=model,
|
model=model,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
funcs=funcs if model.model_entity.abilities.__contains__('func_call') else [],
|
funcs=funcs if model.model_entity.abilities.__contains__('func_call') else [],
|
||||||
extra_args=extra_args,
|
extra_args=extra_args,
|
||||||
)
|
)
|
||||||
return msg, model
|
# invoke_llm returns (message, usage_info) tuple
|
||||||
|
if isinstance(result, tuple) and len(result) == 2:
|
||||||
|
msg, usage_info = result
|
||||||
|
else:
|
||||||
|
msg = result
|
||||||
|
usage_info = {}
|
||||||
|
return msg, model, usage_info
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
last_error = e
|
last_error = e
|
||||||
logger.warning(f'[LLM:{self.node_id}] Model {model.model_entity.name} failed: {e}, trying next...')
|
logger.warning(f'[LLM:{self.node_id}] Model {model.model_entity.name} failed: {e}, trying next...')
|
||||||
@@ -514,7 +520,7 @@ Respond in the same language as the user's input.
|
|||||||
|
|
||||||
# Invoke LLM with fallback
|
# Invoke LLM with fallback
|
||||||
try:
|
try:
|
||||||
result_message, used_model = await self._invoke_with_fallback(
|
result_message, used_model, llm_usage = await self._invoke_with_fallback(
|
||||||
candidates=candidates,
|
candidates=candidates,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
funcs=None,
|
funcs=None,
|
||||||
@@ -579,25 +585,31 @@ Respond in the same language as the user's input.
|
|||||||
'blocked_by_filter': True,
|
'blocked_by_filter': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Extract usage info
|
# Extract usage info from LLM call result
|
||||||
if hasattr(result_message, 'usage') and result_message.usage:
|
# Priority: llm_usage (from _invoke_with_fallback) > result_message.usage > result_message.token_usage
|
||||||
|
if llm_usage:
|
||||||
|
usage = {
|
||||||
|
'prompt_tokens': llm_usage.get('input_tokens', 0) or llm_usage.get('prompt_tokens', 0),
|
||||||
|
'completion_tokens': llm_usage.get('output_tokens', 0) or llm_usage.get('completion_tokens', 0),
|
||||||
|
'total_tokens': llm_usage.get('total_tokens', 0),
|
||||||
|
}
|
||||||
|
# Check result_message.usage (set by RuntimeProvider.invoke_llm)
|
||||||
|
elif hasattr(result_message, 'usage') and result_message.usage:
|
||||||
u = result_message.usage
|
u = result_message.usage
|
||||||
# Handle both object and dict usage
|
|
||||||
if isinstance(u, dict):
|
if isinstance(u, dict):
|
||||||
usage = {
|
usage = {
|
||||||
'prompt_tokens': u.get('prompt_tokens', 0) or 0,
|
'prompt_tokens': u.get('input_tokens', 0) or u.get('prompt_tokens', 0),
|
||||||
'completion_tokens': u.get('completion_tokens', 0) or 0,
|
'completion_tokens': u.get('output_tokens', 0) or u.get('completion_tokens', 0),
|
||||||
'total_tokens': u.get('total_tokens', 0) or 0,
|
'total_tokens': u.get('total_tokens', 0),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
usage = {
|
usage = {
|
||||||
'prompt_tokens': getattr(u, 'prompt_tokens', 0) or 0,
|
'prompt_tokens': getattr(u, 'input_tokens', 0) or getattr(u, 'prompt_tokens', 0),
|
||||||
'completion_tokens': getattr(u, 'completion_tokens', 0) or 0,
|
'completion_tokens': getattr(u, 'output_tokens', 0) or getattr(u, 'completion_tokens', 0),
|
||||||
'total_tokens': getattr(u, 'total_tokens', 0) or 0,
|
'total_tokens': getattr(u, 'total_tokens', 0),
|
||||||
}
|
}
|
||||||
elif hasattr(result_message, 'token_usage') and result_message.token_usage:
|
elif hasattr(result_message, 'token_usage') and result_message.token_usage:
|
||||||
u = result_message.token_usage
|
u = result_message.token_usage
|
||||||
# Handle both object and dict token_usage
|
|
||||||
if isinstance(u, dict):
|
if isinstance(u, dict):
|
||||||
usage = {
|
usage = {
|
||||||
'prompt_tokens': u.get('prompt_tokens', 0) or 0,
|
'prompt_tokens': u.get('prompt_tokens', 0) or 0,
|
||||||
|
|||||||
@@ -320,6 +320,7 @@ export default function BotForm({
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (initBotId) {
|
if (initBotId) {
|
||||||
const formValues = form.getValues();
|
const formValues = form.getValues();
|
||||||
|
const bindingType = formValues.binding_type ?? 'pipeline';
|
||||||
const updateBot: Bot = {
|
const updateBot: Bot = {
|
||||||
uuid: initBotId,
|
uuid: initBotId,
|
||||||
name: formValues.name,
|
name: formValues.name,
|
||||||
@@ -327,13 +328,13 @@ export default function BotForm({
|
|||||||
adapter: formValues.adapter,
|
adapter: formValues.adapter,
|
||||||
adapter_config: formValues.adapter_config,
|
adapter_config: formValues.adapter_config,
|
||||||
enable: formValues.enable,
|
enable: formValues.enable,
|
||||||
binding_type: formValues.binding_type ?? 'pipeline',
|
binding_type: bindingType,
|
||||||
binding_uuid: formValues.binding_uuid ?? '',
|
binding_uuid: formValues.binding_uuid ?? '',
|
||||||
// Sync use_pipeline_uuid for backward compatibility when binding_type is 'pipeline'
|
// Only send use_pipeline_uuid when binding_type is 'pipeline'
|
||||||
use_pipeline_uuid:
|
// For 'workflow' binding, we don't need this field
|
||||||
formValues.binding_type === 'pipeline'
|
...(bindingType === 'pipeline' && {
|
||||||
? formValues.binding_uuid
|
use_pipeline_uuid: formValues.binding_uuid,
|
||||||
: formValues.use_pipeline_uuid,
|
}),
|
||||||
};
|
};
|
||||||
httpClient
|
httpClient
|
||||||
.updateBot(initBotId, updateBot)
|
.updateBot(initBotId, updateBot)
|
||||||
|
|||||||
Reference in New Issue
Block a user