echo-agent 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {echo_agent-0.2.1 → echo_agent-0.2.2}/PKG-INFO +4 -1
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/__init__.py +42 -25
- echo_agent-0.2.2/echo_agent/agent/tools/image_gen_fal.py +231 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/base.py +15 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/dingtalk.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/email.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/feishu.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/matrix.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/qqbot.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/webhook.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/wecom.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/weixin.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/whatsapp.py +2 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/i18n/en.py +8 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/i18n/zh.py +8 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/setup.py +32 -10
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/config/schema.py +8 -1
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/dependencies/lazy_deps.py +1 -0
- echo_agent-0.2.2/echo_agent/gateway/api/__init__.py +50 -0
- echo_agent-0.2.2/echo_agent/gateway/api/channels.py +53 -0
- echo_agent-0.2.2/echo_agent/gateway/api/config.py +85 -0
- echo_agent-0.2.2/echo_agent/gateway/api/knowledge.py +136 -0
- echo_agent-0.2.2/echo_agent/gateway/api/memory.py +137 -0
- echo_agent-0.2.2/echo_agent/gateway/api/skills.py +66 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/server.py +4 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/capabilities.py +2 -2
- {echo_agent-0.2.1 → echo_agent-0.2.2}/pyproject.toml +3 -2
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/ppt-author/scripts/create_pptx.py +15 -10
- {echo_agent-0.2.1 → echo_agent-0.2.2}/.gitignore +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/LICENSE +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/README.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/__main__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/a2a/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/a2a/client.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/a2a/models.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/a2a/protocol.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/a2a/server.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/approval_gate.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/assembler.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/boundary.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/compressor.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/engine.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/pruner.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/summarizer.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/types.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/compression/validator.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/consolidation.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/context.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/context_cache.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/executors/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/executors/base.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/executors/factory.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/executors/remote.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/loop.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/multi_agent/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/multi_agent/audit.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/multi_agent/error_messages.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/multi_agent/error_types.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/multi_agent/models.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/multi_agent/registry.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/multi_agent/runtime.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/pipeline/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/pipeline/context_stage.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/pipeline/inference_stage.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/pipeline/response_stage.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/pipeline/types.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/planning/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/planning/models.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/planning/planner.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/planning/reflection.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/planning/strategies.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/streaming.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/base.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/circuit_breaker.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/clarify.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/code_exec.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/cronjob.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/delegate.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/filesystem.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/image_gen.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/knowledge.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/memory.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/message.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/notify.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/patch.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/process.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/registry.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/search.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/session_search.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/shell.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/skill_install.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/skills.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/task.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/todo.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/tts.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/vision.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/web.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/agent/tools/workflow.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/app.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/bus/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/bus/events.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/bus/queue.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/bus/rate_limiter.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/cli.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/cron.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/discord.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/qqbot_media.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/slack.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/channels/telegram.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/colors.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/evolution_cmd.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/i18n/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/plugins_cmd.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/prompt.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/service.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/cli/status.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/config/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/config/default.yaml +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/config/loader.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/dependencies/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/dependencies/cli.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/dependencies/skill_require.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evaluation/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evaluation/dataset.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evaluation/metrics.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evaluation/reporter.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evaluation/runner.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/engine.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/evolver.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/gate.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/recorder.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/scheduler.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/store.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/tools.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/types.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/evolution/validation.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/auth.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/editor.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/health.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/hooks.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/media.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/rate_limiter.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/router.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/session_context.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/session_policy.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/gateway/static/index.html +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/knowledge/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/knowledge/index.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/mcp/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/mcp/client.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/mcp/manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/mcp/oauth.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/mcp/security.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/mcp/tool_adapter.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/mcp/transport.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/consolidator.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/contradiction.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/forgetting.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/retrieval.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/reviewer.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/store.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/tiers.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/types.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/memory/vectors.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/credential_pool.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/inference.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/provider.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/providers/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/providers/anthropic_provider.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/providers/bedrock_provider.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/providers/format_utils.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/providers/gemini_provider.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/providers/openai_provider.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/providers/openrouter_provider.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/rate_limiter.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/router.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/stub.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/models/tokenizer.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/observability/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/observability/monitor.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/observability/spans.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/observability/telemetry.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/permissions/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/permissions/allowlist.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/permissions/manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/context.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/errors.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/hooks.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/loader.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/manifest.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/plugins/sandbox.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/runtime_paths.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/scheduler/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/scheduler/delivery.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/scheduler/service.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/guards.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/normalizer.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/path_policy.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/risk_classifier.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/smart_approval.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/tokenizer.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/security/tool_policy.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/session/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/session/manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/session/media_ref.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/skills/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/skills/manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/skills/reviewer.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/skills/store.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/storage/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/storage/backend.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/storage/sqlite.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/tasks/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/tasks/manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/tasks/models.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/tasks/workflow.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/tools/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/tools/base.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/utils/__init__.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/utils/async_io.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/echo_agent/utils/text.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/scripts/install.sh +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/excel-author/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/excel-author/scripts/create_xlsx.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/image-gen/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/image-gen/scripts/generate_image.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/meme-gen/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/meme-gen/scripts/make_meme.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/creative/ppt-author/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/code-runner/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/code-runner/scripts/safe_exec.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/github-ops/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/plan/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/skill-creator/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/skill-creator/scripts/init_skill.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/skill-creator/scripts/package_skill.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/skill-creator/scripts/quick_validate.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/workflow-chain/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/development/workflow-chain/scripts/workflow_engine.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/devops/docker-manage/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/devops/system-monitor/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/devops/system-monitor/scripts/system_check.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/finance/finance-tracker/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/finance/finance-tracker/scripts/finance_manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/finance/stocks/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/finance/stocks/scripts/market_query.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/health/fitness-nutrition/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/health/fitness-nutrition/scripts/health_query.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/learning/flashcards/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/learning/flashcards/scripts/flashcard_engine.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/media/tts-voice/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/media/tts-voice/scripts/text_to_speech.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/media/voice-note/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/media/voice-note/scripts/voice_process.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/calendar/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/calendar/scripts/calendar_client.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/daily-briefing/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/daily-briefing/scripts/generate_briefing.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/email-assistant/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/email-assistant/scripts/email_client.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/note-taking/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/note-taking/scripts/notes_manager.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/notion-sync/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/notion-sync/scripts/notion_client.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/ocr-document/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/ocr-document/scripts/extract_document.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/reminder/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/reminder/scripts/reminder_store.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/summarize/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/productivity/weather/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/arxiv/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/arxiv/scripts/search_arxiv.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/deep-research/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/deep-research/scripts/research_report.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/rss-watcher/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/rss-watcher/scripts/feed_monitor.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/web-extract/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/web-extract/scripts/extract_url.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/web-search/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/research/web-search/scripts/web_search.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/calculator/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/calculator/scripts/calc.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/file-convert/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/file-convert/scripts/convert.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/maps-poi/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/maps-poi/scripts/geo_query.py +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/text-tools/SKILL.md +0 -0
- {echo_agent-0.2.1 → echo_agent-0.2.2}/skills/utility/text-tools/scripts/text_process.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: echo-agent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: A modular AI agent framework with multi-channel support
|
|
5
5
|
Author: Echo Agent contributors
|
|
6
6
|
License: MIT
|
|
@@ -25,6 +25,7 @@ Requires-Dist: anthropic>=0.40; extra == 'all'
|
|
|
25
25
|
Requires-Dist: boto3>=1.34; extra == 'all'
|
|
26
26
|
Requires-Dist: cryptography>=41.0; extra == 'all'
|
|
27
27
|
Requires-Dist: faiss-cpu>=1.7; extra == 'all'
|
|
28
|
+
Requires-Dist: fal-client>=0.5; extra == 'all'
|
|
28
29
|
Requires-Dist: google-generativeai>=0.8; extra == 'all'
|
|
29
30
|
Requires-Dist: openai>=1.30; extra == 'all'
|
|
30
31
|
Requires-Dist: psutil>=5.9; extra == 'all'
|
|
@@ -45,6 +46,8 @@ Provides-Extra: dev
|
|
|
45
46
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
46
47
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
47
48
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
49
|
+
Provides-Extra: fal
|
|
50
|
+
Requires-Dist: fal-client>=0.5; extra == 'fal'
|
|
48
51
|
Provides-Extra: gemini
|
|
49
52
|
Requires-Dist: google-generativeai>=0.8; extra == 'gemini'
|
|
50
53
|
Provides-Extra: openai
|
|
@@ -189,30 +189,42 @@ def _infer_tts_model(api_base: str) -> str:
|
|
|
189
189
|
|
|
190
190
|
def _try_register_image_gen(tools: list[Tool], config: Config, provider: LLMProvider | None = None) -> None:
|
|
191
191
|
ig = getattr(config.tools, "image_gen", None)
|
|
192
|
-
|
|
192
|
+
backend = getattr(ig, "backend", "openai") if ig else "openai"
|
|
193
|
+
|
|
194
|
+
if backend == "fal":
|
|
195
|
+
fal_key = getattr(ig, "fal_key", "") if ig else ""
|
|
196
|
+
fal_model = getattr(ig, "fal_model", "") if ig else ""
|
|
197
|
+
if not fal_key:
|
|
198
|
+
logger.info(
|
|
199
|
+
"image_generate tool not registered: no fal_key configured. "
|
|
200
|
+
"Set tools.image_gen.fal_key and tools.image_gen.fal_model in config to enable."
|
|
201
|
+
)
|
|
202
|
+
return
|
|
203
|
+
from echo_agent.agent.tools.image_gen_fal import FalImageGenTool, FAL_MODELS
|
|
204
|
+
if fal_model and fal_model not in FAL_MODELS:
|
|
205
|
+
logger.warning(
|
|
206
|
+
"image_generate: configured fal_model '{}' is not in the built-in catalog. "
|
|
207
|
+
"Supported: {}. The tool will error at execution time.",
|
|
208
|
+
fal_model, ", ".join(sorted(FAL_MODELS.keys())),
|
|
209
|
+
)
|
|
210
|
+
tools.append(FalImageGenTool(fal_key=fal_key, model=fal_model))
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
api_key = getattr(ig, "api_key", "") if ig else ""
|
|
193
214
|
api_base = getattr(ig, "api_base", "") if ig else ""
|
|
194
215
|
model = getattr(ig, "model", "") if ig else ""
|
|
195
216
|
|
|
196
|
-
if explicit_key:
|
|
197
|
-
# User explicitly configured image_gen — use their settings as-is
|
|
198
|
-
api_key = explicit_key
|
|
199
|
-
elif _is_openai_compatible_provider(provider):
|
|
200
|
-
# Fallback only for OpenAI-compatible providers — use unwrapped to get real key/base
|
|
201
|
-
real = _unwrap_provider(provider)
|
|
202
|
-
api_key = getattr(real, "api_key", "")
|
|
203
|
-
if not api_base:
|
|
204
|
-
api_base = getattr(real, "api_base", "")
|
|
205
|
-
# Reset default model so we infer from api_base
|
|
206
|
-
model = ""
|
|
207
|
-
else:
|
|
208
|
-
return
|
|
209
|
-
|
|
210
217
|
if not api_key:
|
|
218
|
+
if _is_openai_compatible_provider(provider):
|
|
219
|
+
logger.info(
|
|
220
|
+
"image_generate tool not registered: no explicit image_gen.api_key configured. "
|
|
221
|
+
"Set tools.image_gen.api_key, tools.image_gen.api_base, and tools.image_gen.model in config to enable."
|
|
222
|
+
)
|
|
211
223
|
return
|
|
212
224
|
|
|
213
|
-
|
|
214
|
-
if not model or (not explicit_key and model == "dall-e-3"):
|
|
225
|
+
if not model:
|
|
215
226
|
model = _infer_image_model(api_base)
|
|
227
|
+
logger.debug("image_generate: model not configured, inferred '{}' from api_base", model)
|
|
216
228
|
|
|
217
229
|
from echo_agent.agent.tools.image_gen import ImageGenTool
|
|
218
230
|
tools.append(ImageGenTool(api_key=api_key, api_base=api_base, model=model))
|
|
@@ -222,18 +234,23 @@ def _try_register_tts(tools: list[Tool], config: Config, ws: str, provider: LLMP
|
|
|
222
234
|
from echo_agent.agent.tools.tts import TTSTool
|
|
223
235
|
tts_cfg = getattr(config.tools, "tts", None)
|
|
224
236
|
openai_key = getattr(tts_cfg, "openai_api_key", "") if tts_cfg else ""
|
|
225
|
-
openai_base = ""
|
|
237
|
+
openai_base = getattr(tts_cfg, "openai_api_base", "") if tts_cfg else ""
|
|
238
|
+
tts_model = getattr(tts_cfg, "model", "") if tts_cfg else ""
|
|
226
239
|
default_backend = getattr(tts_cfg, "default_backend", "edge") if tts_cfg else "edge"
|
|
227
240
|
default_voice = getattr(tts_cfg, "default_voice", "") if tts_cfg else ""
|
|
228
241
|
|
|
229
|
-
# Fallback only for OpenAI-compatible providers
|
|
230
242
|
if not openai_key and _is_openai_compatible_provider(provider):
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
243
|
+
logger.info(
|
|
244
|
+
"TTS openai backend not available: no explicit tts.openai_api_key configured. "
|
|
245
|
+
"Set tools.tts.openai_api_key, tools.tts.openai_api_base, and tools.tts.model in config to enable. "
|
|
246
|
+
"edge backend remains available without configuration."
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if not tts_model and openai_key:
|
|
250
|
+
tts_model = _infer_tts_model(openai_base)
|
|
251
|
+
logger.debug("TTS: model not configured, inferred '{}' from openai_api_base", tts_model)
|
|
252
|
+
if not tts_model:
|
|
253
|
+
tts_model = "tts-1"
|
|
237
254
|
|
|
238
255
|
tools.append(TTSTool(
|
|
239
256
|
workspace=ws,
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Image generation tool — FAL.ai backend."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
import threading
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from echo_agent.agent.tools.base import Tool, ToolExecutionContext, ToolResult
|
|
11
|
+
|
|
12
|
+
DEFAULT_MODEL = "fal-ai/flux/schnell"
|
|
13
|
+
|
|
14
|
+
FAL_MODELS: dict[str, dict[str, Any]] = {
|
|
15
|
+
"fal-ai/flux/schnell": {
|
|
16
|
+
"display": "FLUX Schnell",
|
|
17
|
+
"size_style": "image_size_preset",
|
|
18
|
+
"sizes": {
|
|
19
|
+
"landscape": "landscape_16_9",
|
|
20
|
+
"square": "square_hd",
|
|
21
|
+
"portrait": "portrait_16_9",
|
|
22
|
+
},
|
|
23
|
+
"defaults": {
|
|
24
|
+
"num_images": 1,
|
|
25
|
+
"output_format": "png",
|
|
26
|
+
"enable_safety_checker": False,
|
|
27
|
+
"sync_mode": True,
|
|
28
|
+
},
|
|
29
|
+
"supports": {
|
|
30
|
+
"prompt", "image_size", "num_images", "output_format",
|
|
31
|
+
"enable_safety_checker", "sync_mode", "seed",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
"fal-ai/flux-2-pro": {
|
|
35
|
+
"display": "FLUX 2 Pro",
|
|
36
|
+
"size_style": "image_size_preset",
|
|
37
|
+
"sizes": {
|
|
38
|
+
"landscape": "landscape_16_9",
|
|
39
|
+
"square": "square_hd",
|
|
40
|
+
"portrait": "portrait_16_9",
|
|
41
|
+
},
|
|
42
|
+
"defaults": {
|
|
43
|
+
"num_inference_steps": 50,
|
|
44
|
+
"guidance_scale": 4.5,
|
|
45
|
+
"num_images": 1,
|
|
46
|
+
"output_format": "png",
|
|
47
|
+
"enable_safety_checker": False,
|
|
48
|
+
"sync_mode": True,
|
|
49
|
+
},
|
|
50
|
+
"supports": {
|
|
51
|
+
"prompt", "image_size", "num_inference_steps", "guidance_scale",
|
|
52
|
+
"num_images", "output_format", "enable_safety_checker",
|
|
53
|
+
"sync_mode", "seed",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
"fal-ai/ideogram/v3": {
|
|
57
|
+
"display": "Ideogram V3",
|
|
58
|
+
"size_style": "image_size_preset",
|
|
59
|
+
"sizes": {
|
|
60
|
+
"landscape": "landscape_16_9",
|
|
61
|
+
"square": "square_hd",
|
|
62
|
+
"portrait": "portrait_16_9",
|
|
63
|
+
},
|
|
64
|
+
"defaults": {
|
|
65
|
+
"rendering_speed": "BALANCED",
|
|
66
|
+
"expand_prompt": True,
|
|
67
|
+
"style": "AUTO",
|
|
68
|
+
},
|
|
69
|
+
"supports": {
|
|
70
|
+
"prompt", "image_size", "rendering_speed", "expand_prompt",
|
|
71
|
+
"style", "seed",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
"fal-ai/recraft/v4/pro/text-to-image": {
|
|
75
|
+
"display": "Recraft V4 Pro",
|
|
76
|
+
"size_style": "image_size_preset",
|
|
77
|
+
"sizes": {
|
|
78
|
+
"landscape": "landscape_16_9",
|
|
79
|
+
"square": "square_hd",
|
|
80
|
+
"portrait": "portrait_16_9",
|
|
81
|
+
},
|
|
82
|
+
"defaults": {
|
|
83
|
+
"enable_safety_checker": False,
|
|
84
|
+
},
|
|
85
|
+
"supports": {
|
|
86
|
+
"prompt", "image_size", "enable_safety_checker",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
"fal-ai/qwen-image": {
|
|
90
|
+
"display": "Qwen Image",
|
|
91
|
+
"size_style": "image_size_preset",
|
|
92
|
+
"sizes": {
|
|
93
|
+
"landscape": "landscape_16_9",
|
|
94
|
+
"square": "square_hd",
|
|
95
|
+
"portrait": "portrait_16_9",
|
|
96
|
+
},
|
|
97
|
+
"defaults": {
|
|
98
|
+
"num_inference_steps": 30,
|
|
99
|
+
"guidance_scale": 2.5,
|
|
100
|
+
"num_images": 1,
|
|
101
|
+
"output_format": "png",
|
|
102
|
+
},
|
|
103
|
+
"supports": {
|
|
104
|
+
"prompt", "image_size", "num_inference_steps", "guidance_scale",
|
|
105
|
+
"num_images", "output_format", "seed", "sync_mode",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
VALID_ASPECT_RATIOS = ("landscape", "square", "portrait")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _load_fal_client():
|
|
114
|
+
try:
|
|
115
|
+
from echo_agent.dependencies import ensure
|
|
116
|
+
ensure("tool.image-gen-fal")
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
119
|
+
import fal_client # type: ignore
|
|
120
|
+
return fal_client
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
_fal_env_lock = threading.Lock()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _build_payload(model_id: str, prompt: str, aspect_ratio: str) -> dict[str, Any]:
|
|
127
|
+
meta = FAL_MODELS[model_id]
|
|
128
|
+
size_style = meta["size_style"]
|
|
129
|
+
sizes = meta["sizes"]
|
|
130
|
+
|
|
131
|
+
aspect = aspect_ratio if aspect_ratio in sizes else "landscape"
|
|
132
|
+
payload: dict[str, Any] = dict(meta.get("defaults", {}))
|
|
133
|
+
payload["prompt"] = prompt.strip()
|
|
134
|
+
|
|
135
|
+
if size_style == "image_size_preset":
|
|
136
|
+
payload["image_size"] = sizes[aspect]
|
|
137
|
+
elif size_style == "aspect_ratio":
|
|
138
|
+
payload["aspect_ratio"] = sizes[aspect]
|
|
139
|
+
|
|
140
|
+
supports = meta["supports"]
|
|
141
|
+
return {k: v for k, v in payload.items() if k in supports}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class FalImageGenTool(Tool):
|
|
145
|
+
name = "image_generate"
|
|
146
|
+
description = "Generate an image from a text prompt using FAL.ai (supports FLUX, Ideogram, Recraft, Qwen and more)."
|
|
147
|
+
parameters = {
|
|
148
|
+
"type": "object",
|
|
149
|
+
"properties": {
|
|
150
|
+
"prompt": {"type": "string", "description": "Text description of the image to generate."},
|
|
151
|
+
"aspect_ratio": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"enum": ["landscape", "square", "portrait"],
|
|
154
|
+
"description": "Image aspect ratio.",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
"required": ["prompt"],
|
|
158
|
+
}
|
|
159
|
+
timeout_seconds = 120
|
|
160
|
+
|
|
161
|
+
def __init__(self, fal_key: str = "", model: str = ""):
|
|
162
|
+
self._fal_key = fal_key
|
|
163
|
+
self._model = model or DEFAULT_MODEL
|
|
164
|
+
|
|
165
|
+
def is_ready(self) -> bool:
|
|
166
|
+
return bool(self._fal_key)
|
|
167
|
+
|
|
168
|
+
def readiness_detail(self) -> tuple[bool, str]:
|
|
169
|
+
if self._fal_key:
|
|
170
|
+
return True, "ok"
|
|
171
|
+
return False, "FAL.ai API key not configured"
|
|
172
|
+
|
|
173
|
+
async def execute(self, params: dict[str, Any], ctx: ToolExecutionContext | None = None) -> ToolResult:
|
|
174
|
+
if not self._fal_key:
|
|
175
|
+
return ToolResult(success=False, error="FAL.ai API key not configured")
|
|
176
|
+
|
|
177
|
+
prompt = params.get("prompt", "").strip()
|
|
178
|
+
if not prompt:
|
|
179
|
+
return ToolResult(success=False, error="prompt is required")
|
|
180
|
+
|
|
181
|
+
aspect_ratio = params.get("aspect_ratio", "landscape").lower().strip()
|
|
182
|
+
if aspect_ratio not in VALID_ASPECT_RATIOS:
|
|
183
|
+
aspect_ratio = "landscape"
|
|
184
|
+
|
|
185
|
+
model_id = self._model
|
|
186
|
+
if model_id not in FAL_MODELS:
|
|
187
|
+
supported = ", ".join(sorted(FAL_MODELS.keys()))
|
|
188
|
+
return ToolResult(
|
|
189
|
+
success=False,
|
|
190
|
+
error=f"Unknown FAL model '{model_id}'. Supported models: {supported}",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
arguments = _build_payload(model_id, prompt, aspect_ratio)
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
fal = _load_fal_client()
|
|
197
|
+
except ImportError:
|
|
198
|
+
return ToolResult(
|
|
199
|
+
success=False,
|
|
200
|
+
error="fal-client package not installed. Run: pip install fal-client",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def _submit_with_key():
|
|
204
|
+
with _fal_env_lock:
|
|
205
|
+
prev_key = os.environ.get("FAL_KEY")
|
|
206
|
+
os.environ["FAL_KEY"] = self._fal_key
|
|
207
|
+
try:
|
|
208
|
+
handler = fal.submit(model_id, arguments=arguments)
|
|
209
|
+
return handler.get()
|
|
210
|
+
finally:
|
|
211
|
+
if prev_key is not None:
|
|
212
|
+
os.environ["FAL_KEY"] = prev_key
|
|
213
|
+
else:
|
|
214
|
+
os.environ.pop("FAL_KEY", None)
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
result = await asyncio.to_thread(_submit_with_key)
|
|
218
|
+
except Exception as e:
|
|
219
|
+
return ToolResult(success=False, error=f"FAL.ai generation failed: {e}")
|
|
220
|
+
|
|
221
|
+
images = result.get("images") if isinstance(result, dict) else None
|
|
222
|
+
if not images:
|
|
223
|
+
return ToolResult(success=False, error="No images returned from FAL.ai")
|
|
224
|
+
|
|
225
|
+
image_url = images[0].get("url", "")
|
|
226
|
+
width = images[0].get("width", "")
|
|
227
|
+
height = images[0].get("height", "")
|
|
228
|
+
output = f"Image URL: {image_url}"
|
|
229
|
+
if width and height:
|
|
230
|
+
output += f"\nSize: {width}x{height}"
|
|
231
|
+
return ToolResult(output=output, metadata={"url": image_url})
|
|
@@ -21,6 +21,7 @@ class SendResult:
|
|
|
21
21
|
success: bool
|
|
22
22
|
message_id: str = ""
|
|
23
23
|
error: str = ""
|
|
24
|
+
skipped: bool = False
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class BaseChannel(ABC):
|
|
@@ -52,6 +53,20 @@ class BaseChannel(ABC):
|
|
|
52
53
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
53
54
|
"""Send a message through this channel."""
|
|
54
55
|
|
|
56
|
+
def should_deliver(self, event: OutboundEvent) -> bool:
|
|
57
|
+
"""Return False to silently skip non-final messages on channels that cannot edit.
|
|
58
|
+
|
|
59
|
+
Channels that support message editing (Telegram, Discord) can show progress
|
|
60
|
+
then overwrite it with the final response. Channels without edit support would
|
|
61
|
+
deliver every intermediate chunk as a separate, irrevocable message — confusing
|
|
62
|
+
the user with duplicates. This guard prevents that at the base layer.
|
|
63
|
+
"""
|
|
64
|
+
if self.supports_edit:
|
|
65
|
+
return True
|
|
66
|
+
if event.is_final:
|
|
67
|
+
return True
|
|
68
|
+
return False
|
|
69
|
+
|
|
55
70
|
async def edit_message(
|
|
56
71
|
self,
|
|
57
72
|
chat_id: str,
|
|
@@ -56,6 +56,8 @@ class DingTalkChannel(BaseChannel):
|
|
|
56
56
|
await self._session.close()
|
|
57
57
|
|
|
58
58
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
59
|
+
if not self.should_deliver(event):
|
|
60
|
+
return SendResult(success=True, skipped=True)
|
|
59
61
|
text = event.text or ""
|
|
60
62
|
if not text or not self._session:
|
|
61
63
|
return SendResult(success=False, error="no text or no session")
|
|
@@ -44,6 +44,8 @@ class EmailChannel(BaseChannel):
|
|
|
44
44
|
pass
|
|
45
45
|
|
|
46
46
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
47
|
+
if not self.should_deliver(event):
|
|
48
|
+
return SendResult(success=True, skipped=True)
|
|
47
49
|
text = event.text or ""
|
|
48
50
|
if not text:
|
|
49
51
|
return SendResult(success=False, error="no text")
|
|
@@ -56,6 +56,8 @@ class FeishuChannel(BaseChannel):
|
|
|
56
56
|
await self._session.close()
|
|
57
57
|
|
|
58
58
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
59
|
+
if not self.should_deliver(event):
|
|
60
|
+
return SendResult(success=True, skipped=True)
|
|
59
61
|
text = event.text or ""
|
|
60
62
|
if not text or not self._session:
|
|
61
63
|
return SendResult(success=False, error="no text or no session")
|
|
@@ -54,6 +54,8 @@ class MatrixChannel(BaseChannel):
|
|
|
54
54
|
await self._session.close()
|
|
55
55
|
|
|
56
56
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
57
|
+
if not self.should_deliver(event):
|
|
58
|
+
return SendResult(success=True, skipped=True)
|
|
57
59
|
text = event.text or ""
|
|
58
60
|
if not text or not self._session:
|
|
59
61
|
return SendResult(success=False, error="no text or no session")
|
|
@@ -191,6 +191,8 @@ class QQBotChannel(BaseChannel):
|
|
|
191
191
|
# ── Send ─────────────────────────────────────────────────────────────────
|
|
192
192
|
|
|
193
193
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
194
|
+
if not self.should_deliver(event):
|
|
195
|
+
return SendResult(success=True, skipped=True)
|
|
194
196
|
if not self._session:
|
|
195
197
|
return SendResult(success=False, error="session not initialized")
|
|
196
198
|
await self._ensure_token()
|
|
@@ -43,6 +43,8 @@ class WebhookChannel(BaseChannel):
|
|
|
43
43
|
await self._runner.cleanup()
|
|
44
44
|
|
|
45
45
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
46
|
+
if not self.should_deliver(event):
|
|
47
|
+
return SendResult(success=True, skipped=True)
|
|
46
48
|
future = self._pending_responses.pop(event.reply_to_id or "", None)
|
|
47
49
|
if future and not future.done():
|
|
48
50
|
future.set_result(event.text)
|
|
@@ -55,6 +55,8 @@ class WeComChannel(BaseChannel):
|
|
|
55
55
|
await self._session.close()
|
|
56
56
|
|
|
57
57
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
58
|
+
if not self.should_deliver(event):
|
|
59
|
+
return SendResult(success=True, skipped=True)
|
|
58
60
|
text = event.text or ""
|
|
59
61
|
if not text or not self._session:
|
|
60
62
|
return SendResult(success=False, error="no text or no session")
|
|
@@ -367,6 +367,8 @@ class WeixinChannel(BaseChannel):
|
|
|
367
367
|
# ── Send ─────────────────────────────────────────────────────────────────
|
|
368
368
|
|
|
369
369
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
370
|
+
if not self.should_deliver(event):
|
|
371
|
+
return SendResult(success=True, skipped=True)
|
|
370
372
|
text = event.text or ""
|
|
371
373
|
if not text or not self._send_session or not self._token:
|
|
372
374
|
return SendResult(success=False, error="no text, no session, or no token")
|
|
@@ -51,6 +51,8 @@ class WhatsAppChannel(BaseChannel):
|
|
|
51
51
|
await self._session.close()
|
|
52
52
|
|
|
53
53
|
async def send(self, event: OutboundEvent) -> SendResult | None:
|
|
54
|
+
if not self.should_deliver(event):
|
|
55
|
+
return SendResult(success=True, skipped=True)
|
|
54
56
|
text = event.text or ""
|
|
55
57
|
if not text or not self._session:
|
|
56
58
|
return SendResult(success=False, error="no text or no session")
|
|
@@ -156,9 +156,17 @@ MESSAGES = {
|
|
|
156
156
|
"web_provider": "Search provider:",
|
|
157
157
|
"web_api_key": "Search API key",
|
|
158
158
|
"image_api_key": "Image-gen API key",
|
|
159
|
+
"image_api_base": "Image-gen API base URL",
|
|
159
160
|
"image_model": "Image-gen model",
|
|
161
|
+
"image_backend": "Image-gen backend:",
|
|
162
|
+
"image_backend_openai": "OpenAI-compatible (SiliconFlow / DashScope / OpenAI)",
|
|
163
|
+
"image_backend_fal": "FAL.ai (FLUX / Ideogram / Recraft / Qwen)",
|
|
164
|
+
"image_fal_key": "FAL.ai API key",
|
|
165
|
+
"image_fal_model": "FAL model (e.g. fal-ai/flux/schnell)",
|
|
160
166
|
"tts_backend": "TTS backend:",
|
|
161
167
|
"tts_openai_key": "OpenAI TTS API key",
|
|
168
|
+
"tts_openai_base": "TTS API base URL",
|
|
169
|
+
"tts_model": "TTS model",
|
|
162
170
|
"mcp_skip_hint": "Configure MCP servers later in echo-agent.yaml under tools.mcp_servers",
|
|
163
171
|
"saved": "Tool profile: {profile}, optional: {extras}",
|
|
164
172
|
},
|
|
@@ -156,9 +156,17 @@ MESSAGES = {
|
|
|
156
156
|
"web_provider": "搜索 Provider:",
|
|
157
157
|
"web_api_key": "搜索 API key",
|
|
158
158
|
"image_api_key": "图像生成 API key",
|
|
159
|
+
"image_api_base": "图像生成 API 地址",
|
|
159
160
|
"image_model": "图像生成模型",
|
|
161
|
+
"image_backend": "图像生成后端:",
|
|
162
|
+
"image_backend_openai": "OpenAI 兼容(硅基流动/通义/智谱/OpenAI)",
|
|
163
|
+
"image_backend_fal": "FAL.ai(FLUX/Ideogram/Recraft/Qwen)",
|
|
164
|
+
"image_fal_key": "FAL.ai API key",
|
|
165
|
+
"image_fal_model": "FAL 模型(如 fal-ai/flux/schnell)",
|
|
160
166
|
"tts_backend": "TTS 后端:",
|
|
161
167
|
"tts_openai_key": "OpenAI TTS API key",
|
|
168
|
+
"tts_openai_base": "TTS API 地址",
|
|
169
|
+
"tts_model": "TTS 模型",
|
|
162
170
|
"mcp_skip_hint": "稍后在 echo-agent.yaml 的 tools.mcp_servers 中配置 MCP。",
|
|
163
171
|
"saved": "工具档位:{profile},可选:{extras}",
|
|
164
172
|
},
|
|
@@ -389,7 +389,7 @@ def setup_tools(config: dict) -> None:
|
|
|
389
389
|
if (tools.get("web", {}) or {}).get("enabled"):
|
|
390
390
|
pre_selected.append(TOOL_OPTIONS.index("web"))
|
|
391
391
|
image_block = tools.get("image_gen") or tools.get("imageGen") or {}
|
|
392
|
-
if image_block.get("api_key") or image_block.get("apiKey"):
|
|
392
|
+
if image_block.get("api_key") or image_block.get("apiKey") or image_block.get("fal_key") or image_block.get("falKey"):
|
|
393
393
|
pre_selected.append(TOOL_OPTIONS.index("image_gen"))
|
|
394
394
|
tts_block = tools.get("tts", {}) or {}
|
|
395
395
|
if tts_block.get("openai_api_key") or tts_block.get("openaiApiKey") or tts_block.get("default_backend"):
|
|
@@ -436,16 +436,36 @@ def setup_tools(config: dict) -> None:
|
|
|
436
436
|
|
|
437
437
|
if "image_gen" in chosen:
|
|
438
438
|
ig = _ensure_dict(tools, "image_gen")
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
439
|
+
backend_options = [t("tools.image_backend_openai"), t("tools.image_backend_fal")]
|
|
440
|
+
backend_values = ["openai", "fal"]
|
|
441
|
+
cur_backend = ig.get("backend", "openai")
|
|
442
|
+
b_idx = prompt_choice(t("tools.image_backend"), backend_options,
|
|
443
|
+
default=backend_values.index(cur_backend) if cur_backend in backend_values else 0)
|
|
444
|
+
ig["backend"] = backend_values[b_idx]
|
|
445
|
+
|
|
446
|
+
if backend_values[b_idx] == "fal":
|
|
447
|
+
existing = ig.get("fal_key") or ig.get("falKey") or ""
|
|
448
|
+
if existing:
|
|
449
|
+
new_key = prompt(f" {t('tools.image_fal_key')} [****{t('common.saved')}]", password=True)
|
|
450
|
+
if new_key:
|
|
451
|
+
ig["fal_key"] = new_key
|
|
452
|
+
else:
|
|
453
|
+
new_key = prompt(f" {t('tools.image_fal_key')}", password=True)
|
|
454
|
+
if new_key:
|
|
455
|
+
ig["fal_key"] = new_key
|
|
456
|
+
ig["fal_model"] = prompt(f" {t('tools.image_fal_model')}", default=ig.get("fal_model") or ig.get("falModel") or "fal-ai/flux/schnell")
|
|
444
457
|
else:
|
|
445
|
-
|
|
446
|
-
if
|
|
447
|
-
|
|
448
|
-
|
|
458
|
+
existing = ig.get("api_key") or ig.get("apiKey") or ""
|
|
459
|
+
if existing:
|
|
460
|
+
new_key = prompt(f" {t('tools.image_api_key')} [****{t('common.saved')}]", password=True)
|
|
461
|
+
if new_key:
|
|
462
|
+
ig["api_key"] = new_key
|
|
463
|
+
else:
|
|
464
|
+
new_key = prompt(f" {t('tools.image_api_key')}", password=True)
|
|
465
|
+
if new_key:
|
|
466
|
+
ig["api_key"] = new_key
|
|
467
|
+
ig["api_base"] = prompt(f" {t('tools.image_api_base')}", default=ig.get("api_base") or ig.get("apiBase") or "https://api.openai.com/v1")
|
|
468
|
+
ig["model"] = prompt(f" {t('tools.image_model')}", default=ig.get("model", "dall-e-3"))
|
|
449
469
|
|
|
450
470
|
if "tts" in chosen:
|
|
451
471
|
tts = _ensure_dict(tools, "tts")
|
|
@@ -462,6 +482,8 @@ def setup_tools(config: dict) -> None:
|
|
|
462
482
|
tts["openai_api_key"] = new_key
|
|
463
483
|
else:
|
|
464
484
|
tts["openai_api_key"] = prompt(f" {t('tools.tts_openai_key')}", password=True)
|
|
485
|
+
tts["openai_api_base"] = prompt(f" {t('tools.tts_openai_base')}", default=tts.get("openai_api_base", "https://api.openai.com/v1"))
|
|
486
|
+
tts["model"] = prompt(f" {t('tools.tts_model')}", default=tts.get("model", "tts-1"))
|
|
465
487
|
|
|
466
488
|
code_exec = _ensure_dict(tools, "code_exec")
|
|
467
489
|
code_exec["enabled"] = "code_exec" in chosen
|
|
@@ -243,13 +243,20 @@ class WebToolConfig(_Base):
|
|
|
243
243
|
|
|
244
244
|
|
|
245
245
|
class ImageGenConfig(_Base):
|
|
246
|
+
backend: str = "openai"
|
|
247
|
+
# OpenAI-compatible backend
|
|
246
248
|
api_key: str = ""
|
|
247
249
|
api_base: str = ""
|
|
248
|
-
model: str = "
|
|
250
|
+
model: str = ""
|
|
251
|
+
# FAL.ai backend
|
|
252
|
+
fal_key: str = ""
|
|
253
|
+
fal_model: str = ""
|
|
249
254
|
|
|
250
255
|
|
|
251
256
|
class TTSConfig(_Base):
|
|
252
257
|
openai_api_key: str = ""
|
|
258
|
+
openai_api_base: str = ""
|
|
259
|
+
model: str = ""
|
|
253
260
|
default_backend: str = "edge"
|
|
254
261
|
default_voice: str = ""
|
|
255
262
|
|
|
@@ -75,6 +75,7 @@ SKILL_DEPS: dict[str, tuple[str, ...]] = {
|
|
|
75
75
|
|
|
76
76
|
# ─── Creative skills ──────────────────────────────────────────────────
|
|
77
77
|
"skill.image-gen": (), # uses API via urllib, no extra deps
|
|
78
|
+
"tool.image-gen-fal": ("fal-client>=0.5",),
|
|
78
79
|
"skill.meme-gen": ("Pillow>=10.0",),
|
|
79
80
|
"skill.ppt-author": ("python-pptx>=1.0",),
|
|
80
81
|
"skill.excel-author": ("openpyxl>=3.1",),
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Management API routes for the desktop client.
|
|
2
|
+
|
|
3
|
+
All routes are registered under the gateway's api_prefix (default /api).
|
|
4
|
+
Authentication reuses the existing GatewayAuth token check.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from aiohttp import web
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from echo_agent.gateway.server import GatewayServer
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_management_routes(app: web.Application, prefix: str, server: GatewayServer) -> None:
|
|
18
|
+
from echo_agent.gateway.api.memory import MemoryAPI
|
|
19
|
+
from echo_agent.gateway.api.skills import SkillsAPI
|
|
20
|
+
from echo_agent.gateway.api.channels import ChannelsAPI
|
|
21
|
+
from echo_agent.gateway.api.knowledge import KnowledgeAPI
|
|
22
|
+
from echo_agent.gateway.api.config import ConfigAPI
|
|
23
|
+
|
|
24
|
+
memory_api = MemoryAPI(server)
|
|
25
|
+
skills_api = SkillsAPI(server)
|
|
26
|
+
channels_api = ChannelsAPI(server)
|
|
27
|
+
knowledge_api = KnowledgeAPI(server)
|
|
28
|
+
config_api = ConfigAPI(server)
|
|
29
|
+
|
|
30
|
+
app.router.add_get(f"{prefix}/memory", memory_api.list_entries)
|
|
31
|
+
app.router.add_get(f"{prefix}/memory/stats", memory_api.stats)
|
|
32
|
+
app.router.add_post(f"{prefix}/memory/search", memory_api.search)
|
|
33
|
+
app.router.add_get(f"{prefix}/memory/{{id}}", memory_api.get_entry)
|
|
34
|
+
app.router.add_put(f"{prefix}/memory/{{id}}", memory_api.update_entry)
|
|
35
|
+
app.router.add_delete(f"{prefix}/memory/{{id}}", memory_api.delete_entry)
|
|
36
|
+
|
|
37
|
+
app.router.add_get(f"{prefix}/skills", skills_api.list_skills)
|
|
38
|
+
app.router.add_get(f"{prefix}/skills/{{name}}", skills_api.get_skill)
|
|
39
|
+
app.router.add_post(f"{prefix}/skills/{{name}}/toggle", skills_api.toggle_skill)
|
|
40
|
+
|
|
41
|
+
app.router.add_get(f"{prefix}/channels", channels_api.list_channels)
|
|
42
|
+
|
|
43
|
+
app.router.add_get(f"{prefix}/knowledge/status", knowledge_api.get_status)
|
|
44
|
+
app.router.add_post(f"{prefix}/knowledge/rebuild", knowledge_api.rebuild)
|
|
45
|
+
app.router.add_post(f"{prefix}/knowledge/upload", knowledge_api.upload)
|
|
46
|
+
app.router.add_get(f"{prefix}/knowledge/documents", knowledge_api.list_documents)
|
|
47
|
+
app.router.add_delete(f"{prefix}/knowledge/documents/{{path}}", knowledge_api.delete_document)
|
|
48
|
+
|
|
49
|
+
app.router.add_get(f"{prefix}/config", config_api.get_config)
|
|
50
|
+
app.router.add_get(f"{prefix}/config/models", config_api.get_models)
|