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,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Embedding Adapter
|
|
3
|
+
=======================
|
|
4
|
+
|
|
5
|
+
Abstract base class for all embedding adapters.
|
|
6
|
+
Defines the contract that all embedding providers must implement.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class EmbeddingRequest:
|
|
16
|
+
"""
|
|
17
|
+
Standard embedding request structure.
|
|
18
|
+
|
|
19
|
+
Provider-agnostic request format. Different providers interpret fields differently:
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
texts: List of texts to embed
|
|
23
|
+
model: Model name to use
|
|
24
|
+
dimensions: Embedding vector dimensions (optional)
|
|
25
|
+
input_type: Input type hint for task-aware embeddings (optional)
|
|
26
|
+
- Cohere: Maps to 'input_type' ("search_document", "search_query", "classification", "clustering")
|
|
27
|
+
- Jina: Maps to 'task' ("retrieval.passage", "retrieval.query", etc.)
|
|
28
|
+
- OpenAI/Ollama: Ignored
|
|
29
|
+
encoding_format: Output format ("float" or "base64", default: "float")
|
|
30
|
+
truncate: Whether to truncate texts that exceed max tokens (default: True)
|
|
31
|
+
normalized: Whether to return L2-normalized embeddings (Jina/Ollama only)
|
|
32
|
+
late_chunking: Enable late chunking for long context (Jina v3 only)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
texts: List[str]
|
|
36
|
+
model: str
|
|
37
|
+
dimensions: Optional[int] = None
|
|
38
|
+
input_type: Optional[str] = None
|
|
39
|
+
encoding_format: Optional[str] = "float"
|
|
40
|
+
truncate: Optional[bool] = True
|
|
41
|
+
normalized: Optional[bool] = True
|
|
42
|
+
late_chunking: Optional[bool] = False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class EmbeddingResponse:
|
|
47
|
+
"""Standard embedding response structure."""
|
|
48
|
+
|
|
49
|
+
embeddings: List[List[float]]
|
|
50
|
+
model: str
|
|
51
|
+
dimensions: int
|
|
52
|
+
usage: Dict[str, Any]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class BaseEmbeddingAdapter(ABC):
|
|
56
|
+
"""
|
|
57
|
+
Base class for all embedding adapters.
|
|
58
|
+
|
|
59
|
+
Each adapter implements the specific API interface for a provider
|
|
60
|
+
(OpenAI, Cohere, Ollama, etc.) while exposing a unified interface.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, config: Dict[str, Any]):
|
|
64
|
+
"""
|
|
65
|
+
Initialize the adapter with configuration.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
config: Dictionary containing:
|
|
69
|
+
- api_key: API authentication key (optional for local)
|
|
70
|
+
- base_url: API endpoint URL
|
|
71
|
+
- model: Model name to use
|
|
72
|
+
- dimensions: Embedding vector dimensions
|
|
73
|
+
- request_timeout: Request timeout in seconds
|
|
74
|
+
"""
|
|
75
|
+
self.api_key = config.get("api_key")
|
|
76
|
+
self.base_url = config.get("base_url")
|
|
77
|
+
self.api_version = config.get("api_version")
|
|
78
|
+
self.model = config.get("model")
|
|
79
|
+
self.dimensions = config.get("dimensions")
|
|
80
|
+
self.request_timeout = config.get("request_timeout", 30)
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
84
|
+
"""
|
|
85
|
+
Generate embeddings for a list of texts.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
request: EmbeddingRequest with texts and parameters
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
EmbeddingResponse with embeddings and metadata
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
httpx.HTTPError: If the API request fails
|
|
95
|
+
"""
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def get_model_info(self) -> Dict[str, Any]:
|
|
100
|
+
"""
|
|
101
|
+
Return information about the configured model.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Dictionary with model metadata (name, dimensions, etc.)
|
|
105
|
+
"""
|
|
106
|
+
pass
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Cohere Embedding Adapter for v1 and v2 API."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .base import BaseEmbeddingAdapter, EmbeddingRequest, EmbeddingResponse
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CohereEmbeddingAdapter(BaseEmbeddingAdapter):
|
|
14
|
+
"""Adapter for Cohere Embed API (v1 and v2)."""
|
|
15
|
+
|
|
16
|
+
MODELS_INFO = {
|
|
17
|
+
"embed-v4.0": {
|
|
18
|
+
"dimensions": [256, 512, 1024, 1536],
|
|
19
|
+
"default": 1024,
|
|
20
|
+
"api_version": "v2",
|
|
21
|
+
},
|
|
22
|
+
"embed-english-v3.0": {
|
|
23
|
+
"dimensions": [1024],
|
|
24
|
+
"default": 1024,
|
|
25
|
+
"api_version": "v1",
|
|
26
|
+
},
|
|
27
|
+
"embed-multilingual-v3.0": {
|
|
28
|
+
"dimensions": [1024],
|
|
29
|
+
"default": 1024,
|
|
30
|
+
"api_version": "v1",
|
|
31
|
+
},
|
|
32
|
+
"embed-multilingual-light-v3.0": {
|
|
33
|
+
"dimensions": [384],
|
|
34
|
+
"default": 384,
|
|
35
|
+
"api_version": "v1",
|
|
36
|
+
},
|
|
37
|
+
"embed-english-light-v3.0": {
|
|
38
|
+
"dimensions": [384],
|
|
39
|
+
"default": 384,
|
|
40
|
+
"api_version": "v1",
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
45
|
+
headers = {
|
|
46
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
model_name = request.model or self.model
|
|
51
|
+
model_info = self.MODELS_INFO.get(model_name, {})
|
|
52
|
+
api_version = model_info.get("api_version", "v2")
|
|
53
|
+
dimension = request.dimensions or self.dimensions
|
|
54
|
+
|
|
55
|
+
input_type = request.input_type or "search_document"
|
|
56
|
+
|
|
57
|
+
if api_version == "v1":
|
|
58
|
+
payload = {
|
|
59
|
+
"texts": request.texts,
|
|
60
|
+
"model": model_name,
|
|
61
|
+
"input_type": input_type,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if not request.truncate:
|
|
65
|
+
payload["truncate"] = "NONE"
|
|
66
|
+
else:
|
|
67
|
+
payload = {
|
|
68
|
+
"texts": request.texts,
|
|
69
|
+
"model": model_name,
|
|
70
|
+
"embedding_types": ["float"],
|
|
71
|
+
"input_type": input_type,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
supported_dims = model_info.get("dimensions", [])
|
|
75
|
+
if isinstance(supported_dims, list) and len(supported_dims) > 1:
|
|
76
|
+
payload["output_dimension"] = dimension or model_info.get("default")
|
|
77
|
+
|
|
78
|
+
if not request.truncate:
|
|
79
|
+
payload["truncate"] = "NONE"
|
|
80
|
+
|
|
81
|
+
url = f"{self.base_url}/{api_version}/embed"
|
|
82
|
+
|
|
83
|
+
logger.debug(f"Sending embedding request to {url} with {len(request.texts)} texts")
|
|
84
|
+
|
|
85
|
+
async with httpx.AsyncClient(timeout=self.request_timeout) as client:
|
|
86
|
+
response = await client.post(url, json=payload, headers=headers)
|
|
87
|
+
|
|
88
|
+
if response.status_code >= 400:
|
|
89
|
+
logger.error(f"HTTP {response.status_code} response body: {response.text}")
|
|
90
|
+
|
|
91
|
+
response.raise_for_status()
|
|
92
|
+
data = response.json()
|
|
93
|
+
|
|
94
|
+
if api_version == "v1":
|
|
95
|
+
embeddings = data["embeddings"]
|
|
96
|
+
else:
|
|
97
|
+
embeddings = data["embeddings"]["float"]
|
|
98
|
+
|
|
99
|
+
actual_dims = len(embeddings[0]) if embeddings else 0
|
|
100
|
+
expected_dims = request.dimensions or self.dimensions
|
|
101
|
+
|
|
102
|
+
if expected_dims and actual_dims != expected_dims:
|
|
103
|
+
logger.warning(f"Dimension mismatch: expected {expected_dims}, got {actual_dims}")
|
|
104
|
+
|
|
105
|
+
logger.info(
|
|
106
|
+
f"Successfully generated {len(embeddings)} embeddings "
|
|
107
|
+
f"(model: {data.get('model', self.model)}, dimensions: {actual_dims})"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return EmbeddingResponse(
|
|
111
|
+
embeddings=embeddings,
|
|
112
|
+
model=data.get("model", self.model),
|
|
113
|
+
dimensions=actual_dims,
|
|
114
|
+
usage=data.get("meta", {}).get("billed_units", {}),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def get_model_info(self) -> Dict[str, Any]:
|
|
118
|
+
model_info = self.MODELS_INFO.get(self.model, {})
|
|
119
|
+
dimensions_list = model_info.get("dimensions", [])
|
|
120
|
+
return {
|
|
121
|
+
"model": self.model,
|
|
122
|
+
"dimensions": model_info.get("default", self.dimensions),
|
|
123
|
+
"supports_variable_dimensions": len(dimensions_list) > 1
|
|
124
|
+
if isinstance(dimensions_list, list)
|
|
125
|
+
else False,
|
|
126
|
+
"provider": "cohere",
|
|
127
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Jina AI embedding adapter with task-aware embeddings and late chunking."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .base import BaseEmbeddingAdapter, EmbeddingRequest, EmbeddingResponse
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class JinaEmbeddingAdapter(BaseEmbeddingAdapter):
|
|
14
|
+
MODELS_INFO = {
|
|
15
|
+
"jina-embeddings-v3": {"default": 1024, "dimensions": [32, 64, 128, 256, 512, 768, 1024]},
|
|
16
|
+
"jina-embeddings-v4": {"default": 1024, "dimensions": [32, 64, 128, 256, 512, 768, 1024]},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
INPUT_TYPE_TO_TASK = {
|
|
20
|
+
"search_document": "retrieval.passage",
|
|
21
|
+
"search_query": "retrieval.query",
|
|
22
|
+
"classification": "classification",
|
|
23
|
+
"clustering": "separation",
|
|
24
|
+
"text-matching": "text-matching",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
28
|
+
headers = {
|
|
29
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
payload = {
|
|
34
|
+
"input": request.texts,
|
|
35
|
+
"model": request.model or self.model,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if request.dimensions:
|
|
39
|
+
payload["dimensions"] = request.dimensions
|
|
40
|
+
elif self.dimensions:
|
|
41
|
+
payload["dimensions"] = self.dimensions
|
|
42
|
+
|
|
43
|
+
if request.input_type:
|
|
44
|
+
task = self.INPUT_TYPE_TO_TASK.get(request.input_type, request.input_type)
|
|
45
|
+
payload["task"] = task
|
|
46
|
+
logger.debug(f"Using Jina task: {task}")
|
|
47
|
+
|
|
48
|
+
if request.normalized is not None:
|
|
49
|
+
payload["normalized"] = request.normalized
|
|
50
|
+
|
|
51
|
+
if request.late_chunking:
|
|
52
|
+
payload["late_chunking"] = True
|
|
53
|
+
|
|
54
|
+
url = f"{self.base_url}/embeddings"
|
|
55
|
+
|
|
56
|
+
logger.debug(f"Sending embedding request to {url} with {len(request.texts)} texts")
|
|
57
|
+
|
|
58
|
+
async with httpx.AsyncClient(timeout=self.request_timeout) as client:
|
|
59
|
+
response = await client.post(url, json=payload, headers=headers)
|
|
60
|
+
|
|
61
|
+
if response.status_code >= 400:
|
|
62
|
+
logger.error(f"HTTP {response.status_code} response body: {response.text}")
|
|
63
|
+
|
|
64
|
+
response.raise_for_status()
|
|
65
|
+
data = response.json()
|
|
66
|
+
|
|
67
|
+
embeddings = [item["embedding"] for item in data["data"]]
|
|
68
|
+
actual_dims = len(embeddings[0]) if embeddings else 0
|
|
69
|
+
|
|
70
|
+
logger.info(
|
|
71
|
+
f"Successfully generated {len(embeddings)} embeddings "
|
|
72
|
+
f"(model: {data['model']}, dimensions: {actual_dims})"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return EmbeddingResponse(
|
|
76
|
+
embeddings=embeddings,
|
|
77
|
+
model=data["model"],
|
|
78
|
+
dimensions=actual_dims,
|
|
79
|
+
usage=data.get("usage", {}),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def get_model_info(self) -> Dict[str, Any]:
|
|
83
|
+
model_info = self.MODELS_INFO.get(self.model, self.dimensions)
|
|
84
|
+
|
|
85
|
+
if isinstance(model_info, dict):
|
|
86
|
+
return {
|
|
87
|
+
"model": self.model,
|
|
88
|
+
"dimensions": model_info.get("default", self.dimensions),
|
|
89
|
+
"supported_dimensions": model_info.get("dimensions", []),
|
|
90
|
+
"supports_variable_dimensions": True,
|
|
91
|
+
"provider": "jina",
|
|
92
|
+
}
|
|
93
|
+
else:
|
|
94
|
+
return {
|
|
95
|
+
"model": self.model,
|
|
96
|
+
"dimensions": model_info or self.dimensions,
|
|
97
|
+
"supports_variable_dimensions": False,
|
|
98
|
+
"provider": "jina",
|
|
99
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Ollama Embedding Adapter for local embeddings."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .base import BaseEmbeddingAdapter, EmbeddingRequest, EmbeddingResponse
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OllamaEmbeddingAdapter(BaseEmbeddingAdapter):
|
|
14
|
+
MODELS_INFO = {
|
|
15
|
+
"all-minilm": 384,
|
|
16
|
+
"all-mpnet-base-v2": 768,
|
|
17
|
+
"nomic-embed-text": 768,
|
|
18
|
+
"mxbai-embed-large": 1024,
|
|
19
|
+
"snowflake-arctic-embed": 1024,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
23
|
+
payload = {
|
|
24
|
+
"model": request.model or self.model,
|
|
25
|
+
"input": request.texts,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if request.dimensions or self.dimensions:
|
|
29
|
+
payload["dimensions"] = request.dimensions or self.dimensions
|
|
30
|
+
|
|
31
|
+
if request.truncate is not None:
|
|
32
|
+
payload["truncate"] = request.truncate
|
|
33
|
+
|
|
34
|
+
payload["keep_alive"] = "5m"
|
|
35
|
+
|
|
36
|
+
url = f"{self.base_url}/api/embed"
|
|
37
|
+
|
|
38
|
+
logger.debug(f"Sending embedding request to {url} with {len(request.texts)} texts")
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
async with httpx.AsyncClient(timeout=self.request_timeout) as client:
|
|
42
|
+
response = await client.post(url, json=payload)
|
|
43
|
+
|
|
44
|
+
if response.status_code == 404:
|
|
45
|
+
try:
|
|
46
|
+
health_check = await client.get(f"{self.base_url}/api/tags")
|
|
47
|
+
if health_check.status_code == 200:
|
|
48
|
+
available_models = [
|
|
49
|
+
m.get("name", "") for m in health_check.json().get("models", [])
|
|
50
|
+
]
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"Model '{payload['model']}' not found in Ollama. "
|
|
53
|
+
f"Available models: {', '.join(available_models[:10])}. "
|
|
54
|
+
f"Download it with: ollama pull {payload['model']}"
|
|
55
|
+
)
|
|
56
|
+
except httpx.HTTPError:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"Model '{payload['model']}' not found. "
|
|
61
|
+
f"Download it with: ollama pull {payload['model']}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
response.raise_for_status()
|
|
65
|
+
data = response.json()
|
|
66
|
+
|
|
67
|
+
except httpx.ConnectError as e:
|
|
68
|
+
raise ConnectionError(
|
|
69
|
+
f"Cannot connect to Ollama at {self.base_url}. "
|
|
70
|
+
f"Make sure Ollama is running. Start it with: ollama serve"
|
|
71
|
+
) from e
|
|
72
|
+
|
|
73
|
+
except httpx.TimeoutException as e:
|
|
74
|
+
raise TimeoutError(
|
|
75
|
+
f"Request to Ollama timed out after {self.request_timeout}s. "
|
|
76
|
+
f"The model might be too large or the server is overloaded."
|
|
77
|
+
) from e
|
|
78
|
+
|
|
79
|
+
except httpx.HTTPError as e:
|
|
80
|
+
logger.error(f"Ollama API error: {e}")
|
|
81
|
+
raise
|
|
82
|
+
|
|
83
|
+
embeddings = data["embeddings"]
|
|
84
|
+
|
|
85
|
+
actual_dims = len(embeddings[0]) if embeddings else 0
|
|
86
|
+
expected_dims = request.dimensions or self.dimensions
|
|
87
|
+
|
|
88
|
+
if expected_dims and actual_dims != expected_dims:
|
|
89
|
+
logger.warning(
|
|
90
|
+
f"Dimension mismatch: expected {expected_dims}, got {actual_dims}. "
|
|
91
|
+
f"Model '{payload['model']}' may not support custom dimensions."
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
logger.info(
|
|
95
|
+
f"Successfully generated {len(embeddings)} embeddings "
|
|
96
|
+
f"(model: {data.get('model', self.model)}, dimensions: {actual_dims})"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return EmbeddingResponse(
|
|
100
|
+
embeddings=embeddings,
|
|
101
|
+
model=data.get("model", self.model),
|
|
102
|
+
dimensions=actual_dims,
|
|
103
|
+
usage={
|
|
104
|
+
"prompt_eval_count": data.get("prompt_eval_count", 0),
|
|
105
|
+
"total_duration": data.get("total_duration", 0),
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def get_model_info(self) -> Dict[str, Any]:
|
|
110
|
+
return {
|
|
111
|
+
"model": self.model,
|
|
112
|
+
"dimensions": self.MODELS_INFO.get(self.model, self.dimensions),
|
|
113
|
+
"local": True,
|
|
114
|
+
"supports_variable_dimensions": False,
|
|
115
|
+
"provider": "ollama",
|
|
116
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""OpenAI-compatible embedding adapter for OpenAI, Azure, HuggingFace, LM Studio, etc."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .base import BaseEmbeddingAdapter, EmbeddingRequest, EmbeddingResponse
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OpenAICompatibleEmbeddingAdapter(BaseEmbeddingAdapter):
|
|
14
|
+
MODELS_INFO = {
|
|
15
|
+
"text-embedding-3-large": {"default": 3072, "dimensions": [256, 512, 1024, 3072]},
|
|
16
|
+
"text-embedding-3-small": {"default": 1536, "dimensions": [512, 1536]},
|
|
17
|
+
"text-embedding-ada-002": 1536,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async def embed(self, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
21
|
+
headers = {
|
|
22
|
+
"Content-Type": "application/json",
|
|
23
|
+
}
|
|
24
|
+
if self.api_version:
|
|
25
|
+
headers["api-key"] = self.api_key
|
|
26
|
+
else:
|
|
27
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
28
|
+
|
|
29
|
+
payload = {
|
|
30
|
+
"input": request.texts,
|
|
31
|
+
"model": request.model or self.model,
|
|
32
|
+
"encoding_format": request.encoding_format or "float",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if request.dimensions or self.dimensions:
|
|
36
|
+
payload["dimensions"] = request.dimensions or self.dimensions
|
|
37
|
+
|
|
38
|
+
url = f"{self.base_url.rstrip('/')}/embeddings"
|
|
39
|
+
if self.api_version:
|
|
40
|
+
if "?" not in url:
|
|
41
|
+
url += f"?api-version={self.api_version}"
|
|
42
|
+
else:
|
|
43
|
+
url += f"&api-version={self.api_version}"
|
|
44
|
+
|
|
45
|
+
logger.debug(f"Sending embedding request to {url} with {len(request.texts)} texts")
|
|
46
|
+
|
|
47
|
+
async with httpx.AsyncClient(timeout=self.request_timeout) as client:
|
|
48
|
+
response = await client.post(url, json=payload, headers=headers)
|
|
49
|
+
|
|
50
|
+
if response.status_code >= 400:
|
|
51
|
+
logger.error(f"HTTP {response.status_code} response body: {response.text}")
|
|
52
|
+
|
|
53
|
+
response.raise_for_status()
|
|
54
|
+
data = response.json()
|
|
55
|
+
|
|
56
|
+
embeddings = [item["embedding"] for item in data["data"]]
|
|
57
|
+
|
|
58
|
+
actual_dims = len(embeddings[0]) if embeddings else 0
|
|
59
|
+
expected_dims = request.dimensions or self.dimensions
|
|
60
|
+
|
|
61
|
+
if expected_dims and actual_dims != expected_dims:
|
|
62
|
+
logger.warning(
|
|
63
|
+
f"Dimension mismatch: expected {expected_dims}, got {actual_dims}. "
|
|
64
|
+
f"Model '{data['model']}' may not support custom dimensions."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
logger.info(
|
|
68
|
+
f"Successfully generated {len(embeddings)} embeddings "
|
|
69
|
+
f"(model: {data['model']}, dimensions: {actual_dims})"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return EmbeddingResponse(
|
|
73
|
+
embeddings=embeddings,
|
|
74
|
+
model=data["model"],
|
|
75
|
+
dimensions=actual_dims,
|
|
76
|
+
usage=data.get("usage", {}),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def get_model_info(self) -> Dict[str, Any]:
|
|
80
|
+
model_info = self.MODELS_INFO.get(self.model, self.dimensions)
|
|
81
|
+
|
|
82
|
+
if isinstance(model_info, dict):
|
|
83
|
+
return {
|
|
84
|
+
"model": self.model,
|
|
85
|
+
"dimensions": model_info.get("default", self.dimensions),
|
|
86
|
+
"supported_dimensions": model_info.get("dimensions", []),
|
|
87
|
+
"supports_variable_dimensions": len(model_info.get("dimensions", [])) > 1,
|
|
88
|
+
"provider": "openai_compatible",
|
|
89
|
+
}
|
|
90
|
+
else:
|
|
91
|
+
return {
|
|
92
|
+
"model": self.model,
|
|
93
|
+
"dimensions": model_info or self.dimensions,
|
|
94
|
+
"supports_variable_dimensions": False,
|
|
95
|
+
"provider": "openai_compatible",
|
|
96
|
+
}
|