451ad685ae
* feat: integrate DeerFlow agent runner and improve stream handling * refactor: split DeerFlow stream flow and close stale client on reset * fix: enforce max_step and correct timeout type check * fix: harden DeerFlow config parsing and session lifecycle * fix: preserve third-party runner error semantics and harden image parsing * perf: bound DeerFlow values history and seen-id cache * refactor: improve deerflow stream semantics and client lifecycle * fix: harden third-party runner error semantics and fallback aggregation * refactor: reduce deerflow image log noise and lazy-init api session * perf: avoid unnecessary iterable copies in deerflow stream utils * refactor: centralize runner error key and clarify deerflow client lifecycle * refactor: simplify third-party runner output flow * fix: defer streaming runner cleanup and unify error mapping * fix: handle id-less values messages and redact stream payload logs * fix: improve deerflow error signaling and third-party runner flow * fix: support deerflow proxy and refine runner lifecycle * fix: tighten deerflow image validation and runner lifecycle * feat: support deerflow image output components * fix: harden runner stream cleanup and refactor deerflow config * fix: preserve deerflow done hook and simplify runner lifecycle * refactor: simplify third-party runner aggregation and lifecycle closing * fix: preserve first deerflow values payload and simplify runner flow * refactor: unify runner final resolution and harden deerflow close state * refactor: share int coercion and make deerflow close best effort * refactor: extract deerflow mappers and streamline third-party lifecycle * refactor: simplify third-party flow and harden sse flush parsing * fix: make deerflow runner close path best effort * refactor: simplify third-party orchestration and centralize deerflow keys * refactor: simplify third-party chunk flow and deerflow finalization * fix: harden deerflow stream parsing and simplify runner lifecycle * refactor: remove redundant deerflow values text assignment * fix: improve deerflow timeout diagnostics and image input feedback * refactor: flatten third-party runner lifecycle and aggregation * chore: use deerflow official remote svg icon * chore: remove unused deerflow local logo asset
184 lines
6.8 KiB
Python
184 lines
6.8 KiB
Python
import traceback
|
|
|
|
from astrbot.core import astrbot_config, logger
|
|
from astrbot.core.agent.runners.deerflow.constants import (
|
|
DEERFLOW_AGENT_RUNNER_PROVIDER_ID_KEY,
|
|
DEERFLOW_PROVIDER_TYPE,
|
|
)
|
|
from astrbot.core.astrbot_config_mgr import AstrBotConfig, AstrBotConfigManager
|
|
from astrbot.core.db.migration.migra_45_to_46 import migrate_45_to_46
|
|
from astrbot.core.db.migration.migra_token_usage import migrate_token_usage
|
|
from astrbot.core.db.migration.migra_webchat_session import migrate_webchat_session
|
|
|
|
|
|
def _migra_agent_runner_configs(conf: AstrBotConfig, ids_map: dict) -> None:
|
|
"""
|
|
Migra agent runner configs from provider configs.
|
|
"""
|
|
try:
|
|
default_prov_id = conf["provider_settings"]["default_provider_id"]
|
|
if default_prov_id in ids_map:
|
|
conf["provider_settings"]["default_provider_id"] = ""
|
|
p = ids_map[default_prov_id]
|
|
if p["type"] == "dify":
|
|
conf["provider_settings"]["dify_agent_runner_provider_id"] = p["id"]
|
|
conf["provider_settings"]["agent_runner_type"] = "dify"
|
|
elif p["type"] == "coze":
|
|
conf["provider_settings"]["coze_agent_runner_provider_id"] = p["id"]
|
|
conf["provider_settings"]["agent_runner_type"] = "coze"
|
|
elif p["type"] == "dashscope":
|
|
conf["provider_settings"]["dashscope_agent_runner_provider_id"] = p[
|
|
"id"
|
|
]
|
|
conf["provider_settings"]["agent_runner_type"] = "dashscope"
|
|
elif p["type"] == DEERFLOW_PROVIDER_TYPE:
|
|
conf["provider_settings"][DEERFLOW_AGENT_RUNNER_PROVIDER_ID_KEY] = p[
|
|
"id"
|
|
]
|
|
conf["provider_settings"]["agent_runner_type"] = DEERFLOW_PROVIDER_TYPE
|
|
conf.save_config()
|
|
except Exception as e:
|
|
logger.error(f"Migration for third party agent runner configs failed: {e!s}")
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
|
def _migra_provider_to_source_structure(conf: AstrBotConfig) -> None:
|
|
"""
|
|
Migrate old provider structure to new provider-source separation.
|
|
Provider only keeps: id, provider_source_id, model, modalities, custom_extra_body
|
|
All other fields move to provider_sources.
|
|
"""
|
|
providers = conf.get("provider", [])
|
|
provider_sources = conf.get("provider_sources", [])
|
|
|
|
# Track if any migration happened
|
|
migrated = False
|
|
|
|
# Provider-only fields that should stay in provider
|
|
provider_only_fields = {
|
|
"id",
|
|
"provider_source_id",
|
|
"model",
|
|
"modalities",
|
|
"custom_extra_body",
|
|
"enable",
|
|
}
|
|
|
|
# Fields that should not go to source
|
|
source_exclude_fields = provider_only_fields | {"model_config"}
|
|
|
|
for provider in providers:
|
|
# Skip if already has provider_source_id
|
|
if provider.get("provider_source_id"):
|
|
continue
|
|
|
|
# Skip non-chat-completion types (they don't need source separation)
|
|
provider_type = provider.get("provider_type", "")
|
|
if provider_type != "chat_completion":
|
|
# For old types without provider_type, check type field
|
|
old_type = provider.get("type", "")
|
|
if "chat_completion" not in old_type:
|
|
continue
|
|
|
|
migrated = True
|
|
logger.info(f"Migrating provider {provider.get('id')} to new structure")
|
|
|
|
# Extract source fields from provider
|
|
source_fields = {}
|
|
for key, value in list(provider.items()):
|
|
if key not in source_exclude_fields:
|
|
source_fields[key] = value
|
|
|
|
# Create new provider_source
|
|
source_id = provider.get("id", "") + "_source"
|
|
new_source = {"id": source_id, **source_fields}
|
|
|
|
# Update provider to only keep necessary fields
|
|
provider["provider_source_id"] = source_id
|
|
|
|
# Extract model from model_config if exists
|
|
if "model_config" in provider and isinstance(provider["model_config"], dict):
|
|
model_config = provider["model_config"]
|
|
provider["model"] = model_config.get("model", "")
|
|
|
|
# Put other model_config fields into custom_extra_body
|
|
extra_body_fields = {k: v for k, v in model_config.items() if k != "model"}
|
|
if extra_body_fields:
|
|
if "custom_extra_body" not in provider:
|
|
provider["custom_extra_body"] = {}
|
|
provider["custom_extra_body"].update(extra_body_fields)
|
|
|
|
# Initialize new fields if not present
|
|
if "modalities" not in provider:
|
|
provider["modalities"] = []
|
|
if "custom_extra_body" not in provider:
|
|
provider["custom_extra_body"] = {}
|
|
|
|
# Remove fields that should be in source
|
|
keys_to_remove = [k for k in provider.keys() if k not in provider_only_fields]
|
|
for key in keys_to_remove:
|
|
del provider[key]
|
|
|
|
# Add source to provider_sources
|
|
provider_sources.append(new_source)
|
|
|
|
if migrated:
|
|
conf["provider_sources"] = provider_sources
|
|
conf.save_config()
|
|
logger.info("Provider-source structure migration completed")
|
|
|
|
|
|
async def migra(
|
|
db, astrbot_config_mgr, umop_config_router, acm: AstrBotConfigManager
|
|
) -> None:
|
|
"""
|
|
Stores the migration logic here.
|
|
btw, i really don't like migration :(
|
|
"""
|
|
# 4.5 to 4.6 migration for umop_config_router
|
|
try:
|
|
await migrate_45_to_46(astrbot_config_mgr, umop_config_router)
|
|
except Exception as e:
|
|
logger.error(f"Migration from version 4.5 to 4.6 failed: {e!s}")
|
|
logger.error(traceback.format_exc())
|
|
|
|
# migration for webchat session
|
|
try:
|
|
await migrate_webchat_session(db)
|
|
except Exception as e:
|
|
logger.error(f"Migration for webchat session failed: {e!s}")
|
|
logger.error(traceback.format_exc())
|
|
|
|
# migration for token_usage column
|
|
try:
|
|
await migrate_token_usage(db)
|
|
except Exception as e:
|
|
logger.error(f"Migration for token_usage column failed: {e!s}")
|
|
logger.error(traceback.format_exc())
|
|
|
|
# migra third party agent runner configs
|
|
_c = False
|
|
providers = astrbot_config["provider"]
|
|
ids_map = {}
|
|
for prov in providers:
|
|
type_ = prov.get("type")
|
|
if type_ in ["dify", "coze", "dashscope", DEERFLOW_PROVIDER_TYPE]:
|
|
prov["provider_type"] = "agent_runner"
|
|
ids_map[prov["id"]] = {
|
|
"type": type_,
|
|
"id": prov["id"],
|
|
}
|
|
_c = True
|
|
if _c:
|
|
astrbot_config.save_config()
|
|
|
|
for conf in acm.confs.values():
|
|
_migra_agent_runner_configs(conf, ids_map)
|
|
|
|
# Migrate providers to new structure: extract source fields to provider_sources
|
|
try:
|
|
_migra_provider_to_source_structure(astrbot_config)
|
|
except Exception as e:
|
|
logger.error(f"Migration for provider-source structure failed: {e!s}")
|
|
logger.error(traceback.format_exc())
|