realtimex-deeptutor 0.5.0.post1__py3-none-any.whl
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.
- realtimex_deeptutor/__init__.py +67 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/METADATA +1612 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/RECORD +276 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/WHEEL +5 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/entry_points.txt +2 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/licenses/LICENSE +661 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/top_level.txt +2 -0
- src/__init__.py +40 -0
- src/agents/__init__.py +24 -0
- src/agents/base_agent.py +657 -0
- src/agents/chat/__init__.py +24 -0
- src/agents/chat/chat_agent.py +435 -0
- src/agents/chat/prompts/en/chat_agent.yaml +35 -0
- src/agents/chat/prompts/zh/chat_agent.yaml +35 -0
- src/agents/chat/session_manager.py +311 -0
- src/agents/co_writer/__init__.py +0 -0
- src/agents/co_writer/edit_agent.py +260 -0
- src/agents/co_writer/narrator_agent.py +423 -0
- src/agents/co_writer/prompts/en/edit_agent.yaml +113 -0
- src/agents/co_writer/prompts/en/narrator_agent.yaml +88 -0
- src/agents/co_writer/prompts/zh/edit_agent.yaml +113 -0
- src/agents/co_writer/prompts/zh/narrator_agent.yaml +88 -0
- src/agents/guide/__init__.py +16 -0
- src/agents/guide/agents/__init__.py +11 -0
- src/agents/guide/agents/chat_agent.py +104 -0
- src/agents/guide/agents/interactive_agent.py +223 -0
- src/agents/guide/agents/locate_agent.py +149 -0
- src/agents/guide/agents/summary_agent.py +150 -0
- src/agents/guide/guide_manager.py +500 -0
- src/agents/guide/prompts/en/chat_agent.yaml +41 -0
- src/agents/guide/prompts/en/interactive_agent.yaml +202 -0
- src/agents/guide/prompts/en/locate_agent.yaml +68 -0
- src/agents/guide/prompts/en/summary_agent.yaml +157 -0
- src/agents/guide/prompts/zh/chat_agent.yaml +41 -0
- src/agents/guide/prompts/zh/interactive_agent.yaml +626 -0
- src/agents/guide/prompts/zh/locate_agent.yaml +68 -0
- src/agents/guide/prompts/zh/summary_agent.yaml +157 -0
- src/agents/ideagen/__init__.py +12 -0
- src/agents/ideagen/idea_generation_workflow.py +426 -0
- src/agents/ideagen/material_organizer_agent.py +173 -0
- src/agents/ideagen/prompts/en/idea_generation.yaml +187 -0
- src/agents/ideagen/prompts/en/material_organizer.yaml +69 -0
- src/agents/ideagen/prompts/zh/idea_generation.yaml +187 -0
- src/agents/ideagen/prompts/zh/material_organizer.yaml +69 -0
- src/agents/question/__init__.py +24 -0
- src/agents/question/agents/__init__.py +18 -0
- src/agents/question/agents/generate_agent.py +381 -0
- src/agents/question/agents/relevance_analyzer.py +207 -0
- src/agents/question/agents/retrieve_agent.py +239 -0
- src/agents/question/coordinator.py +718 -0
- src/agents/question/example.py +109 -0
- src/agents/question/prompts/en/coordinator.yaml +75 -0
- src/agents/question/prompts/en/generate_agent.yaml +77 -0
- src/agents/question/prompts/en/relevance_analyzer.yaml +41 -0
- src/agents/question/prompts/en/retrieve_agent.yaml +32 -0
- src/agents/question/prompts/zh/coordinator.yaml +75 -0
- src/agents/question/prompts/zh/generate_agent.yaml +77 -0
- src/agents/question/prompts/zh/relevance_analyzer.yaml +39 -0
- src/agents/question/prompts/zh/retrieve_agent.yaml +30 -0
- src/agents/research/agents/__init__.py +23 -0
- src/agents/research/agents/decompose_agent.py +507 -0
- src/agents/research/agents/manager_agent.py +228 -0
- src/agents/research/agents/note_agent.py +180 -0
- src/agents/research/agents/rephrase_agent.py +263 -0
- src/agents/research/agents/reporting_agent.py +1333 -0
- src/agents/research/agents/research_agent.py +714 -0
- src/agents/research/data_structures.py +451 -0
- src/agents/research/main.py +188 -0
- src/agents/research/prompts/en/decompose_agent.yaml +89 -0
- src/agents/research/prompts/en/manager_agent.yaml +24 -0
- src/agents/research/prompts/en/note_agent.yaml +121 -0
- src/agents/research/prompts/en/rephrase_agent.yaml +58 -0
- src/agents/research/prompts/en/reporting_agent.yaml +380 -0
- src/agents/research/prompts/en/research_agent.yaml +173 -0
- src/agents/research/prompts/zh/decompose_agent.yaml +89 -0
- src/agents/research/prompts/zh/manager_agent.yaml +24 -0
- src/agents/research/prompts/zh/note_agent.yaml +121 -0
- src/agents/research/prompts/zh/rephrase_agent.yaml +58 -0
- src/agents/research/prompts/zh/reporting_agent.yaml +380 -0
- src/agents/research/prompts/zh/research_agent.yaml +173 -0
- src/agents/research/research_pipeline.py +1309 -0
- src/agents/research/utils/__init__.py +60 -0
- src/agents/research/utils/citation_manager.py +799 -0
- src/agents/research/utils/json_utils.py +98 -0
- src/agents/research/utils/token_tracker.py +297 -0
- src/agents/solve/__init__.py +80 -0
- src/agents/solve/analysis_loop/__init__.py +14 -0
- src/agents/solve/analysis_loop/investigate_agent.py +414 -0
- src/agents/solve/analysis_loop/note_agent.py +190 -0
- src/agents/solve/main_solver.py +862 -0
- src/agents/solve/memory/__init__.py +34 -0
- src/agents/solve/memory/citation_memory.py +353 -0
- src/agents/solve/memory/investigate_memory.py +226 -0
- src/agents/solve/memory/solve_memory.py +340 -0
- src/agents/solve/prompts/en/analysis_loop/investigate_agent.yaml +55 -0
- src/agents/solve/prompts/en/analysis_loop/note_agent.yaml +54 -0
- src/agents/solve/prompts/en/solve_loop/manager_agent.yaml +67 -0
- src/agents/solve/prompts/en/solve_loop/precision_answer_agent.yaml +62 -0
- src/agents/solve/prompts/en/solve_loop/response_agent.yaml +90 -0
- src/agents/solve/prompts/en/solve_loop/solve_agent.yaml +75 -0
- src/agents/solve/prompts/en/solve_loop/tool_agent.yaml +38 -0
- src/agents/solve/prompts/zh/analysis_loop/investigate_agent.yaml +53 -0
- src/agents/solve/prompts/zh/analysis_loop/note_agent.yaml +54 -0
- src/agents/solve/prompts/zh/solve_loop/manager_agent.yaml +66 -0
- src/agents/solve/prompts/zh/solve_loop/precision_answer_agent.yaml +62 -0
- src/agents/solve/prompts/zh/solve_loop/response_agent.yaml +90 -0
- src/agents/solve/prompts/zh/solve_loop/solve_agent.yaml +76 -0
- src/agents/solve/prompts/zh/solve_loop/tool_agent.yaml +41 -0
- src/agents/solve/solve_loop/__init__.py +22 -0
- src/agents/solve/solve_loop/citation_manager.py +74 -0
- src/agents/solve/solve_loop/manager_agent.py +274 -0
- src/agents/solve/solve_loop/precision_answer_agent.py +96 -0
- src/agents/solve/solve_loop/response_agent.py +301 -0
- src/agents/solve/solve_loop/solve_agent.py +325 -0
- src/agents/solve/solve_loop/tool_agent.py +470 -0
- src/agents/solve/utils/__init__.py +64 -0
- src/agents/solve/utils/config_validator.py +313 -0
- src/agents/solve/utils/display_manager.py +223 -0
- src/agents/solve/utils/error_handler.py +363 -0
- src/agents/solve/utils/json_utils.py +98 -0
- src/agents/solve/utils/performance_monitor.py +407 -0
- src/agents/solve/utils/token_tracker.py +541 -0
- src/api/__init__.py +0 -0
- src/api/main.py +240 -0
- src/api/routers/__init__.py +1 -0
- src/api/routers/agent_config.py +69 -0
- src/api/routers/chat.py +296 -0
- src/api/routers/co_writer.py +337 -0
- src/api/routers/config.py +627 -0
- src/api/routers/dashboard.py +18 -0
- src/api/routers/guide.py +337 -0
- src/api/routers/ideagen.py +436 -0
- src/api/routers/knowledge.py +821 -0
- src/api/routers/notebook.py +247 -0
- src/api/routers/question.py +537 -0
- src/api/routers/research.py +394 -0
- src/api/routers/settings.py +164 -0
- src/api/routers/solve.py +305 -0
- src/api/routers/system.py +252 -0
- src/api/run_server.py +61 -0
- src/api/utils/history.py +172 -0
- src/api/utils/log_interceptor.py +21 -0
- src/api/utils/notebook_manager.py +415 -0
- src/api/utils/progress_broadcaster.py +72 -0
- src/api/utils/task_id_manager.py +100 -0
- src/config/__init__.py +0 -0
- src/config/accessors.py +18 -0
- src/config/constants.py +34 -0
- src/config/defaults.py +18 -0
- src/config/schema.py +38 -0
- src/config/settings.py +50 -0
- src/core/errors.py +62 -0
- src/knowledge/__init__.py +23 -0
- src/knowledge/add_documents.py +606 -0
- src/knowledge/config.py +65 -0
- src/knowledge/example_add_documents.py +236 -0
- src/knowledge/extract_numbered_items.py +1039 -0
- src/knowledge/initializer.py +621 -0
- src/knowledge/kb.py +22 -0
- src/knowledge/manager.py +782 -0
- src/knowledge/progress_tracker.py +182 -0
- src/knowledge/start_kb.py +535 -0
- src/logging/__init__.py +103 -0
- src/logging/adapters/__init__.py +17 -0
- src/logging/adapters/lightrag.py +184 -0
- src/logging/adapters/llamaindex.py +141 -0
- src/logging/config.py +80 -0
- src/logging/handlers/__init__.py +20 -0
- src/logging/handlers/console.py +75 -0
- src/logging/handlers/file.py +201 -0
- src/logging/handlers/websocket.py +127 -0
- src/logging/logger.py +709 -0
- src/logging/stats/__init__.py +16 -0
- src/logging/stats/llm_stats.py +179 -0
- src/services/__init__.py +56 -0
- src/services/config/__init__.py +61 -0
- src/services/config/knowledge_base_config.py +210 -0
- src/services/config/loader.py +260 -0
- src/services/config/unified_config.py +603 -0
- src/services/embedding/__init__.py +45 -0
- src/services/embedding/adapters/__init__.py +22 -0
- src/services/embedding/adapters/base.py +106 -0
- src/services/embedding/adapters/cohere.py +127 -0
- src/services/embedding/adapters/jina.py +99 -0
- src/services/embedding/adapters/ollama.py +116 -0
- src/services/embedding/adapters/openai_compatible.py +96 -0
- src/services/embedding/client.py +159 -0
- src/services/embedding/config.py +156 -0
- src/services/embedding/provider.py +119 -0
- src/services/llm/__init__.py +152 -0
- src/services/llm/capabilities.py +313 -0
- src/services/llm/client.py +302 -0
- src/services/llm/cloud_provider.py +530 -0
- src/services/llm/config.py +200 -0
- src/services/llm/error_mapping.py +103 -0
- src/services/llm/exceptions.py +152 -0
- src/services/llm/factory.py +450 -0
- src/services/llm/local_provider.py +347 -0
- src/services/llm/providers/anthropic.py +95 -0
- src/services/llm/providers/base_provider.py +93 -0
- src/services/llm/providers/open_ai.py +83 -0
- src/services/llm/registry.py +71 -0
- src/services/llm/telemetry.py +40 -0
- src/services/llm/types.py +27 -0
- src/services/llm/utils.py +333 -0
- src/services/prompt/__init__.py +25 -0
- src/services/prompt/manager.py +206 -0
- src/services/rag/__init__.py +64 -0
- src/services/rag/components/__init__.py +29 -0
- src/services/rag/components/base.py +59 -0
- src/services/rag/components/chunkers/__init__.py +18 -0
- src/services/rag/components/chunkers/base.py +34 -0
- src/services/rag/components/chunkers/fixed.py +71 -0
- src/services/rag/components/chunkers/numbered_item.py +94 -0
- src/services/rag/components/chunkers/semantic.py +97 -0
- src/services/rag/components/embedders/__init__.py +14 -0
- src/services/rag/components/embedders/base.py +32 -0
- src/services/rag/components/embedders/openai.py +63 -0
- src/services/rag/components/indexers/__init__.py +18 -0
- src/services/rag/components/indexers/base.py +35 -0
- src/services/rag/components/indexers/graph.py +172 -0
- src/services/rag/components/indexers/lightrag.py +156 -0
- src/services/rag/components/indexers/vector.py +146 -0
- src/services/rag/components/parsers/__init__.py +18 -0
- src/services/rag/components/parsers/base.py +35 -0
- src/services/rag/components/parsers/markdown.py +52 -0
- src/services/rag/components/parsers/pdf.py +115 -0
- src/services/rag/components/parsers/text.py +86 -0
- src/services/rag/components/retrievers/__init__.py +18 -0
- src/services/rag/components/retrievers/base.py +34 -0
- src/services/rag/components/retrievers/dense.py +200 -0
- src/services/rag/components/retrievers/hybrid.py +164 -0
- src/services/rag/components/retrievers/lightrag.py +169 -0
- src/services/rag/components/routing.py +286 -0
- src/services/rag/factory.py +234 -0
- src/services/rag/pipeline.py +215 -0
- src/services/rag/pipelines/__init__.py +32 -0
- src/services/rag/pipelines/academic.py +44 -0
- src/services/rag/pipelines/lightrag.py +43 -0
- src/services/rag/pipelines/llamaindex.py +313 -0
- src/services/rag/pipelines/raganything.py +384 -0
- src/services/rag/service.py +244 -0
- src/services/rag/types.py +73 -0
- src/services/search/__init__.py +284 -0
- src/services/search/base.py +87 -0
- src/services/search/consolidation.py +398 -0
- src/services/search/providers/__init__.py +128 -0
- src/services/search/providers/baidu.py +188 -0
- src/services/search/providers/exa.py +194 -0
- src/services/search/providers/jina.py +161 -0
- src/services/search/providers/perplexity.py +153 -0
- src/services/search/providers/serper.py +209 -0
- src/services/search/providers/tavily.py +161 -0
- src/services/search/types.py +114 -0
- src/services/setup/__init__.py +34 -0
- src/services/setup/init.py +285 -0
- src/services/tts/__init__.py +16 -0
- src/services/tts/config.py +99 -0
- src/tools/__init__.py +91 -0
- src/tools/code_executor.py +536 -0
- src/tools/paper_search_tool.py +171 -0
- src/tools/query_item_tool.py +310 -0
- src/tools/question/__init__.py +15 -0
- src/tools/question/exam_mimic.py +616 -0
- src/tools/question/pdf_parser.py +211 -0
- src/tools/question/question_extractor.py +397 -0
- src/tools/rag_tool.py +173 -0
- src/tools/tex_chunker.py +339 -0
- src/tools/tex_downloader.py +253 -0
- src/tools/web_search.py +71 -0
- src/utils/config_manager.py +206 -0
- src/utils/document_validator.py +168 -0
- src/utils/error_rate_tracker.py +111 -0
- src/utils/error_utils.py +82 -0
- src/utils/json_parser.py +110 -0
- src/utils/network/circuit_breaker.py +79 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import sys
|
|
3
|
+
import traceback
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter, HTTPException
|
|
7
|
+
from fastapi.responses import Response
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
# Ensure co_writer module can be imported
|
|
11
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
|
12
|
+
if str(project_root) not in sys.path:
|
|
13
|
+
sys.path.insert(0, str(project_root))
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
|
|
17
|
+
from src.agents.co_writer.edit_agent import (
|
|
18
|
+
TOOL_CALLS_DIR,
|
|
19
|
+
EditAgent,
|
|
20
|
+
load_history,
|
|
21
|
+
print_stats,
|
|
22
|
+
)
|
|
23
|
+
from src.agents.co_writer.narrator_agent import NarratorAgent
|
|
24
|
+
from src.logging import get_logger
|
|
25
|
+
from src.services.config import load_config_with_main
|
|
26
|
+
from src.services.tts import get_tts_config
|
|
27
|
+
|
|
28
|
+
router = APIRouter()
|
|
29
|
+
|
|
30
|
+
# Initialize logger with config
|
|
31
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
|
32
|
+
config = load_config_with_main("solve_config.yaml", project_root) # Use any config to get main.yaml
|
|
33
|
+
log_dir = config.get("paths", {}).get("user_log_dir") or config.get("logging", {}).get("log_dir")
|
|
34
|
+
logger = get_logger("CoWriter", level="INFO", log_dir=log_dir)
|
|
35
|
+
|
|
36
|
+
# Get system language for agent
|
|
37
|
+
_system_language = config.get("system", {}).get("language", "en")
|
|
38
|
+
|
|
39
|
+
# Singleton agent instances - use refresh_config() before each request
|
|
40
|
+
# to pick up any configuration changes from Settings
|
|
41
|
+
_edit_agent: EditAgent | None = None
|
|
42
|
+
_narrator_agent: NarratorAgent | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_edit_agent() -> EditAgent:
|
|
46
|
+
"""
|
|
47
|
+
Get the singleton EditAgent instance with refreshed configuration.
|
|
48
|
+
|
|
49
|
+
Uses a singleton pattern with refresh_config() to ensure:
|
|
50
|
+
1. Efficient reuse of the agent instance
|
|
51
|
+
2. Latest LLM configuration from Settings is always used
|
|
52
|
+
"""
|
|
53
|
+
global _edit_agent
|
|
54
|
+
if _edit_agent is None:
|
|
55
|
+
_edit_agent = EditAgent(language=_system_language)
|
|
56
|
+
# Refresh config to pick up any changes from Settings
|
|
57
|
+
_edit_agent.refresh_config()
|
|
58
|
+
return _edit_agent
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_narrator_agent() -> NarratorAgent:
|
|
62
|
+
"""
|
|
63
|
+
Get the singleton NarratorAgent instance with refreshed configuration.
|
|
64
|
+
|
|
65
|
+
Uses a singleton pattern with refresh_config() to ensure:
|
|
66
|
+
1. Efficient reuse of the agent instance
|
|
67
|
+
2. Latest LLM configuration from Settings is always used
|
|
68
|
+
"""
|
|
69
|
+
global _narrator_agent
|
|
70
|
+
if _narrator_agent is None:
|
|
71
|
+
_narrator_agent = NarratorAgent(language=_system_language)
|
|
72
|
+
# Refresh config to pick up any changes from Settings
|
|
73
|
+
_narrator_agent.refresh_config()
|
|
74
|
+
return _narrator_agent
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class EditRequest(BaseModel):
|
|
78
|
+
text: str
|
|
79
|
+
instruction: str
|
|
80
|
+
action: Literal["rewrite", "shorten", "expand"] = "rewrite"
|
|
81
|
+
source: Literal["rag", "web"] | None = None
|
|
82
|
+
kb_name: str | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class EditResponse(BaseModel):
|
|
86
|
+
edited_text: str
|
|
87
|
+
operation_id: str
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AutoMarkRequest(BaseModel):
|
|
91
|
+
text: str
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class AutoMarkResponse(BaseModel):
|
|
95
|
+
marked_text: str
|
|
96
|
+
operation_id: str
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@router.post("/edit", response_model=EditResponse)
|
|
100
|
+
async def edit_text(request: EditRequest):
|
|
101
|
+
try:
|
|
102
|
+
# Get agent with refreshed LLM configuration from Settings
|
|
103
|
+
agent = get_edit_agent()
|
|
104
|
+
|
|
105
|
+
result = await agent.process(
|
|
106
|
+
text=request.text,
|
|
107
|
+
instruction=request.instruction,
|
|
108
|
+
action=request.action,
|
|
109
|
+
source=request.source,
|
|
110
|
+
kb_name=request.kb_name,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Print token stats
|
|
114
|
+
print_stats()
|
|
115
|
+
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
traceback.print_exc()
|
|
120
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@router.post("/automark", response_model=AutoMarkResponse)
|
|
124
|
+
async def auto_mark_text(request: AutoMarkRequest):
|
|
125
|
+
"""AI auto-mark text"""
|
|
126
|
+
try:
|
|
127
|
+
# Get agent with refreshed LLM configuration from Settings
|
|
128
|
+
agent = get_edit_agent()
|
|
129
|
+
|
|
130
|
+
result = await agent.auto_mark(text=request.text)
|
|
131
|
+
|
|
132
|
+
# Print token stats
|
|
133
|
+
print_stats()
|
|
134
|
+
|
|
135
|
+
return result
|
|
136
|
+
except Exception as e:
|
|
137
|
+
traceback.print_exc()
|
|
138
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@router.get("/history")
|
|
142
|
+
async def get_history():
|
|
143
|
+
"""Get all operation history"""
|
|
144
|
+
try:
|
|
145
|
+
history = load_history()
|
|
146
|
+
return {"history": history, "total": len(history)}
|
|
147
|
+
except Exception as e:
|
|
148
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@router.get("/history/{operation_id}")
|
|
152
|
+
async def get_operation(operation_id: str):
|
|
153
|
+
"""Get single operation details"""
|
|
154
|
+
try:
|
|
155
|
+
history = load_history()
|
|
156
|
+
for op in history:
|
|
157
|
+
if op.get("id") == operation_id:
|
|
158
|
+
return op
|
|
159
|
+
raise HTTPException(status_code=404, detail="Operation not found")
|
|
160
|
+
except HTTPException:
|
|
161
|
+
raise
|
|
162
|
+
except Exception as e:
|
|
163
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@router.get("/tool_calls/{operation_id}")
|
|
167
|
+
async def get_tool_call(operation_id: str):
|
|
168
|
+
"""Get tool call details"""
|
|
169
|
+
try:
|
|
170
|
+
# Find matching file
|
|
171
|
+
for filepath in TOOL_CALLS_DIR.glob(f"{operation_id}_*.json"):
|
|
172
|
+
with open(filepath, encoding="utf-8") as f:
|
|
173
|
+
return json.load(f)
|
|
174
|
+
raise HTTPException(status_code=404, detail="Tool call not found")
|
|
175
|
+
except HTTPException:
|
|
176
|
+
raise
|
|
177
|
+
except Exception as e:
|
|
178
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@router.post("/export/markdown")
|
|
182
|
+
async def export_markdown(content: dict):
|
|
183
|
+
"""Export as Markdown file"""
|
|
184
|
+
try:
|
|
185
|
+
markdown_content = content.get("content", "")
|
|
186
|
+
filename = content.get("filename", "document.md")
|
|
187
|
+
|
|
188
|
+
return Response(
|
|
189
|
+
content=markdown_content,
|
|
190
|
+
media_type="text/markdown",
|
|
191
|
+
headers={"Content-Disposition": f"attachment; filename={filename}"},
|
|
192
|
+
)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ================= TTS Narration Feature =================
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class NarrateRequest(BaseModel):
|
|
201
|
+
"""Narration request"""
|
|
202
|
+
|
|
203
|
+
content: str
|
|
204
|
+
style: Literal["friendly", "academic", "concise"] = "friendly"
|
|
205
|
+
voice: str | None = None # If None, will use default value from config
|
|
206
|
+
skip_audio: bool = False
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class NarrateResponse(BaseModel):
|
|
210
|
+
"""Narration response"""
|
|
211
|
+
|
|
212
|
+
script: str
|
|
213
|
+
key_points: list[str]
|
|
214
|
+
style: str
|
|
215
|
+
original_length: int
|
|
216
|
+
script_length: int
|
|
217
|
+
has_audio: bool
|
|
218
|
+
audio_url: str | None = None
|
|
219
|
+
audio_id: str | None = None
|
|
220
|
+
voice: str | None = None
|
|
221
|
+
audio_error: str | None = None
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class ScriptOnlyRequest(BaseModel):
|
|
225
|
+
"""Script-only generation request"""
|
|
226
|
+
|
|
227
|
+
content: str
|
|
228
|
+
style: Literal["friendly", "academic", "concise"] = "friendly"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@router.post("/narrate", response_model=NarrateResponse)
|
|
232
|
+
async def narrate_content(request: NarrateRequest):
|
|
233
|
+
"""
|
|
234
|
+
Generate note narration script and optionally generate TTS audio
|
|
235
|
+
|
|
236
|
+
- style: Narration style
|
|
237
|
+
- friendly: Friendly and approachable tutor style
|
|
238
|
+
- academic: Rigorous academic lecture style
|
|
239
|
+
- concise: Efficient and concise knowledge delivery style
|
|
240
|
+
- voice: TTS voice role (alloy, echo, fable, onyx, nova, shimmer)
|
|
241
|
+
- skip_audio: Whether to skip audio generation (set to true to return only script)
|
|
242
|
+
"""
|
|
243
|
+
try:
|
|
244
|
+
narrator = get_narrator_agent()
|
|
245
|
+
result = await narrator.narrate(
|
|
246
|
+
content=request.content,
|
|
247
|
+
style=request.style,
|
|
248
|
+
voice=request.voice,
|
|
249
|
+
skip_audio=request.skip_audio,
|
|
250
|
+
)
|
|
251
|
+
return result
|
|
252
|
+
except ValueError as e:
|
|
253
|
+
# TTS configuration related error
|
|
254
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
255
|
+
except Exception as e:
|
|
256
|
+
traceback.print_exc()
|
|
257
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@router.post("/narrate/script")
|
|
261
|
+
async def generate_script_only(request: ScriptOnlyRequest):
|
|
262
|
+
"""
|
|
263
|
+
Generate script only (no audio generation)
|
|
264
|
+
|
|
265
|
+
Fast endpoint, suitable for previewing script effect
|
|
266
|
+
"""
|
|
267
|
+
try:
|
|
268
|
+
narrator = get_narrator_agent()
|
|
269
|
+
result = await narrator.generate_script(content=request.content, style=request.style)
|
|
270
|
+
return result
|
|
271
|
+
except Exception as e:
|
|
272
|
+
traceback.print_exc()
|
|
273
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@router.get("/tts/status")
|
|
277
|
+
async def get_tts_status():
|
|
278
|
+
"""
|
|
279
|
+
Check TTS service status
|
|
280
|
+
|
|
281
|
+
Returns whether TTS configuration is available
|
|
282
|
+
"""
|
|
283
|
+
try:
|
|
284
|
+
tts_config = get_tts_config()
|
|
285
|
+
return {
|
|
286
|
+
"available": True,
|
|
287
|
+
"model": tts_config.get("model"),
|
|
288
|
+
"default_voice": tts_config.get("voice", "alloy"),
|
|
289
|
+
}
|
|
290
|
+
except ValueError as e:
|
|
291
|
+
return {
|
|
292
|
+
"available": False,
|
|
293
|
+
"error": str(e),
|
|
294
|
+
"hint": "Please configure TTS_MODEL, TTS_API_KEY, TTS_URL in .env file",
|
|
295
|
+
}
|
|
296
|
+
except Exception as e:
|
|
297
|
+
return {"available": False, "error": str(e)}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@router.get("/tts/voices")
|
|
301
|
+
async def get_available_voices():
|
|
302
|
+
"""
|
|
303
|
+
Get available TTS voice role list (OpenAI TTS voices)
|
|
304
|
+
"""
|
|
305
|
+
voices = [
|
|
306
|
+
{
|
|
307
|
+
"id": "alloy",
|
|
308
|
+
"name": "Alloy",
|
|
309
|
+
"description": "Neutral and balanced voice",
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"id": "echo",
|
|
313
|
+
"name": "Echo",
|
|
314
|
+
"description": "Warm and conversational voice",
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"id": "fable",
|
|
318
|
+
"name": "Fable",
|
|
319
|
+
"description": "Expressive and dramatic voice",
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"id": "onyx",
|
|
323
|
+
"name": "Onyx",
|
|
324
|
+
"description": "Deep and authoritative voice",
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"id": "nova",
|
|
328
|
+
"name": "Nova",
|
|
329
|
+
"description": "Friendly and upbeat voice",
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"id": "shimmer",
|
|
333
|
+
"name": "Shimmer",
|
|
334
|
+
"description": "Clear and pleasant voice",
|
|
335
|
+
},
|
|
336
|
+
]
|
|
337
|
+
return {"voices": voices}
|