h-ai-brain 0.0.23__tar.gz → 0.0.24__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.
- {h_ai_brain-0.0.23/src/h_ai_brain.egg-info → h_ai_brain-0.0.24}/PKG-INFO +2 -1
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/pyproject.toml +3 -2
- h_ai_brain-0.0.24/src/h_ai/__init__.py +12 -0
- h_ai_brain-0.0.24/src/h_ai/application/__init__.py +12 -0
- h_ai_brain-0.0.24/src/h_ai/application/hai_service.py +12 -0
- h_ai_brain-0.0.24/src/h_ai/application/services/__init__.py +11 -0
- h_ai_brain-0.0.24/src/h_ai/application/services/base_model_service.py +69 -0
- h_ai_brain-0.0.24/src/h_ai/application/services/granite_service.py +139 -0
- h_ai_brain-0.0.24/src/h_ai/application/services/nomic_service.py +117 -0
- h_ai_brain-0.0.24/src/h_ai/domain/llm_config.py +20 -0
- h_ai_brain-0.0.24/src/h_ai/domain/model_factory.py +44 -0
- h_ai_brain-0.0.24/src/h_ai/domain/reasoning/llm_chat_repository.py +46 -0
- h_ai_brain-0.0.24/src/h_ai/domain/reasoning/llm_embedding_repository.py +20 -0
- h_ai_brain-0.0.24/src/h_ai/domain/reasoning/llm_generate_respository.py +23 -0
- h_ai_brain-0.0.24/src/h_ai/domain/reasoning/llm_tool_repository.py +37 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/json_resource_loader.py +97 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/factories/__init__.py +1 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/factories/granite_factory.py +91 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/factories/nomic_factory.py +58 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/ollama_chat_repository.py +195 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/ollama_embed_repository.py +43 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/ollama_generate_repository.py +119 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/ollama_http_client.py +54 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/prompt_loader.py +53 -0
- h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/template_loader.py +146 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24/src/h_ai_brain.egg-info}/PKG-INFO +2 -1
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai_brain.egg-info/SOURCES.txt +13 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai_brain.egg-info/requires.txt +1 -0
- h_ai_brain-0.0.23/src/h_ai/__init__.py +0 -3
- h_ai_brain-0.0.23/src/h_ai/application/hai_service.py +0 -18
- h_ai_brain-0.0.23/src/h_ai/domain/llm_config.py +0 -7
- h_ai_brain-0.0.23/src/h_ai/domain/reasoning/llm_chat_repository.py +0 -9
- h_ai_brain-0.0.23/src/h_ai/domain/reasoning/llm_generate_respository.py +0 -6
- h_ai_brain-0.0.23/src/h_ai/domain/reasoning/llm_tool_repository.py +0 -14
- h_ai_brain-0.0.23/src/h_ai/infrastructure/llm/ollama/models/__init__.py +0 -0
- h_ai_brain-0.0.23/src/h_ai/infrastructure/llm/ollama/ollama_chat_repository.py +0 -56
- h_ai_brain-0.0.23/src/h_ai/infrastructure/llm/ollama/ollama_generate_repository.py +0 -63
- h_ai_brain-0.0.23/src/h_ai/infrastructure/llm/prompt_loader.py +0 -18
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/LICENSE +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/NOTICE.txt +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/setup.cfg +0 -0
- {h_ai_brain-0.0.23/src/h_ai/application → h_ai_brain-0.0.24/src/h_ai/domain}/__init__.py +0 -0
- {h_ai_brain-0.0.23/src/h_ai/domain → h_ai_brain-0.0.24/src/h_ai/domain/reasoning}/__init__.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/domain/reasoning/text_analysis.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/domain/reasoning/tool_message.py +0 -0
- {h_ai_brain-0.0.23/src/h_ai/domain/reasoning → h_ai_brain-0.0.24/src/h_ai/infrastructure}/__init__.py +0 -0
- {h_ai_brain-0.0.23/src/h_ai/infrastructure → h_ai_brain-0.0.24/src/h_ai/infrastructure/llm}/__init__.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/infrastructure/llm/data_handler.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/infrastructure/llm/llm_response_cleaner.py +0 -0
- {h_ai_brain-0.0.23/src/h_ai/infrastructure/llm → h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama}/__init__.py +0 -0
- {h_ai_brain-0.0.23/src/h_ai/infrastructure/llm/ollama → h_ai_brain-0.0.24/src/h_ai/infrastructure/llm/ollama/models}/__init__.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/infrastructure/llm/ollama/models/ollama_chat_message.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/infrastructure/llm/ollama/models/ollama_chat_session.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/infrastructure/llm/ollama/ollama_tool_repository.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai/infrastructure/llm/prompt_helper.py +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai_brain.egg-info/dependency_links.txt +0 -0
- {h_ai_brain-0.0.23 → h_ai_brain-0.0.24}/src/h_ai_brain.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: h_ai_brain
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.24
|
4
4
|
Summary: AI Research agent API
|
5
5
|
Author-email: shoebill <shoebill.hai@gmail.com>
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
@@ -11,6 +11,7 @@ Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
12
12
|
License-File: NOTICE.txt
|
13
13
|
Requires-Dist: requests~=2.32.3
|
14
|
+
Requires-Dist: jinja2~=3.1.3
|
14
15
|
Provides-Extra: dev
|
15
16
|
Requires-Dist: pytest; extra == "dev"
|
16
17
|
Dynamic: license-file
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "h_ai_brain"
|
7
|
-
version = "0.0.
|
7
|
+
version = "0.0.24"
|
8
8
|
authors = [
|
9
9
|
{name = "shoebill", email = "shoebill.hai@gmail.com"},
|
10
10
|
]
|
@@ -17,7 +17,8 @@
|
|
17
17
|
"Operating System :: OS Independent",
|
18
18
|
]
|
19
19
|
dependencies = [
|
20
|
-
"requests~=2.32.3"
|
20
|
+
"requests~=2.32.3",
|
21
|
+
"jinja2~=3.1.3"
|
21
22
|
]
|
22
23
|
|
23
24
|
[project.optional-dependencies]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
h_ai package for interacting with LLM models.
|
3
|
+
|
4
|
+
This package provides a high-level API for interacting with LLM models.
|
5
|
+
Users should import from this package, not from the application, domain, or infrastructure layers directly.
|
6
|
+
"""
|
7
|
+
|
8
|
+
__all__ = ['GraniteService', 'NomicService', 'BaseModelService']
|
9
|
+
|
10
|
+
from .application.services.granite_service import GraniteService
|
11
|
+
from .application.services.nomic_service import NomicService
|
12
|
+
from .application.services.base_model_service import BaseModelService
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
Application layer for the h_ai package.
|
3
|
+
|
4
|
+
This package contains the public API for the h_ai package.
|
5
|
+
Users should only import from this package, not from the domain or infrastructure layers.
|
6
|
+
"""
|
7
|
+
|
8
|
+
__all__ = ['GraniteService', 'NomicService', 'BaseModelService']
|
9
|
+
|
10
|
+
from .services.granite_service import GraniteService
|
11
|
+
from .services.nomic_service import NomicService
|
12
|
+
from .services.base_model_service import BaseModelService
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
This file has been intentionally emptied as part of the removal of HaiService.
|
3
|
+
HaiService has been deprecated in favor of using GraniteService and NomicService directly.
|
4
|
+
|
5
|
+
The correct model names are:
|
6
|
+
- nomic-embed-text:137m-v1.5-fp16
|
7
|
+
- granite3.3:8b
|
8
|
+
|
9
|
+
Please use the model-specific services directly:
|
10
|
+
- GraniteService for text generation and chat
|
11
|
+
- NomicService for embeddings
|
12
|
+
"""
|
@@ -0,0 +1,11 @@
|
|
1
|
+
"""
|
2
|
+
Model-specific services for the h_ai package.
|
3
|
+
|
4
|
+
This package contains service classes for specific LLM models.
|
5
|
+
"""
|
6
|
+
|
7
|
+
__all__ = ['GraniteService', 'NomicService', 'BaseModelService']
|
8
|
+
|
9
|
+
from .granite_service import GraniteService
|
10
|
+
from .nomic_service import NomicService
|
11
|
+
from .base_model_service import BaseModelService
|
@@ -0,0 +1,69 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Dict, List, Optional
|
3
|
+
|
4
|
+
|
5
|
+
class BaseModelService(ABC):
|
6
|
+
"""
|
7
|
+
Base class for model-specific services.
|
8
|
+
Defines the common interface that all model services must implement.
|
9
|
+
"""
|
10
|
+
|
11
|
+
@abstractmethod
|
12
|
+
def generate(self, prompt: str, system_prompt: str = None, max_tokens: int = None) -> Optional[str]:
|
13
|
+
"""
|
14
|
+
Generate text using the model.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
prompt: The prompt to generate text from.
|
18
|
+
system_prompt: Optional system prompt to use.
|
19
|
+
max_tokens: Optional maximum number of tokens to generate.
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
Optional[str]: The generated text, or None if an error occurs.
|
23
|
+
"""
|
24
|
+
pass
|
25
|
+
|
26
|
+
@abstractmethod
|
27
|
+
def chat(self, message: str, session_id: str, chat_history: List[Dict[str, str]] = None) -> Optional[str]:
|
28
|
+
"""
|
29
|
+
Chat with the model.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
message: The user's message.
|
33
|
+
session_id: The ID of the chat session.
|
34
|
+
chat_history: Optional chat history to include in the conversation.
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
Optional[str]: The model's response, or None if an error occurs.
|
38
|
+
"""
|
39
|
+
pass
|
40
|
+
|
41
|
+
def embed(self, text: str) -> Optional[List[float]]:
|
42
|
+
"""
|
43
|
+
Create an embedding for the given text.
|
44
|
+
Default implementation returns None as not all models support embeddings.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
text: The text to create an embedding for.
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
Optional[List[float]]: The embedding vector, or None if the model doesn't support embeddings.
|
51
|
+
"""
|
52
|
+
return None
|
53
|
+
|
54
|
+
def chat_with_documents(self, message: str, session_id: str, documents: List[Dict[str, str]],
|
55
|
+
chat_history: List[Dict[str, str]] = None) -> Optional[str]:
|
56
|
+
"""
|
57
|
+
Chat with the model using provided documents as context.
|
58
|
+
Default implementation returns None as not all models support document-based chat.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
message: The user's message.
|
62
|
+
session_id: The ID of the chat session.
|
63
|
+
documents: List of documents to use as context.
|
64
|
+
chat_history: Optional chat history to include in the conversation.
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
Optional[str]: The model's response, or None if the model doesn't support document-based chat.
|
68
|
+
"""
|
69
|
+
return None
|
@@ -0,0 +1,139 @@
|
|
1
|
+
from typing import Dict, List, Optional
|
2
|
+
|
3
|
+
from .base_model_service import BaseModelService
|
4
|
+
from ...infrastructure.llm.ollama.factories.granite_factory import GraniteModelFactory
|
5
|
+
from ...infrastructure.llm.ollama.models.ollama_chat_message import OllamaChatMessage
|
6
|
+
|
7
|
+
|
8
|
+
class GraniteService(BaseModelService):
|
9
|
+
"""
|
10
|
+
Service for interacting with the Granite model.
|
11
|
+
Provides specialized methods for the Granite model, including document support.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, api_url: str, api_token: str = None, temperature: float = 0.6, max_tokens: int = 2500):
|
15
|
+
"""
|
16
|
+
Initialize a new GraniteService.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
api_url: The base URL of the LLM API.
|
20
|
+
api_token: Optional API token for authentication.
|
21
|
+
temperature: The temperature to use for generation.
|
22
|
+
max_tokens: The maximum number of tokens to generate.
|
23
|
+
|
24
|
+
Raises:
|
25
|
+
ValueError: If the api_url is empty or None, or if temperature or max_tokens are invalid.
|
26
|
+
"""
|
27
|
+
if not api_url:
|
28
|
+
raise ValueError("API URL cannot be empty or None")
|
29
|
+
if temperature < 0:
|
30
|
+
raise ValueError("Temperature must be non-negative")
|
31
|
+
if max_tokens <= 0:
|
32
|
+
raise ValueError("Max tokens must be positive")
|
33
|
+
|
34
|
+
self.api_url = api_url
|
35
|
+
self.api_token = api_token
|
36
|
+
self.temperature = temperature
|
37
|
+
self.max_tokens = max_tokens
|
38
|
+
|
39
|
+
# Create the Granite model factory
|
40
|
+
self.factory = GraniteModelFactory(
|
41
|
+
api_url=api_url,
|
42
|
+
temperature=temperature,
|
43
|
+
max_tokens=max_tokens,
|
44
|
+
api_token=api_token,
|
45
|
+
use_templating=True
|
46
|
+
)
|
47
|
+
|
48
|
+
# Create repositories
|
49
|
+
self.chat_repository = self.factory.create_chat_repository()
|
50
|
+
self.generate_repository = self.factory.create_generate_repository()
|
51
|
+
|
52
|
+
def generate(self, prompt: str, system_prompt: str = None, max_tokens: int = None) -> Optional[str]:
|
53
|
+
"""
|
54
|
+
Generate text using the Granite model.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
prompt: The prompt to generate text from.
|
58
|
+
system_prompt: Optional system prompt to use.
|
59
|
+
max_tokens: Optional maximum number of tokens to generate.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
Optional[str]: The generated text, or None if an error occurs.
|
63
|
+
|
64
|
+
Raises:
|
65
|
+
ValueError: If the prompt is empty or None.
|
66
|
+
"""
|
67
|
+
if not prompt:
|
68
|
+
raise ValueError("Prompt cannot be empty or None")
|
69
|
+
|
70
|
+
return self.generate_repository.generate(prompt, system_prompt, max_tokens)
|
71
|
+
|
72
|
+
def chat(self, message: str, session_id: str, chat_history: List[Dict[str, str]] = None) -> Optional[str]:
|
73
|
+
"""
|
74
|
+
Chat with the Granite model.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
message: The user's message.
|
78
|
+
session_id: The ID of the chat session.
|
79
|
+
chat_history: Optional chat history to include in the conversation.
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
Optional[str]: The model's response, or None if an error occurs.
|
83
|
+
|
84
|
+
Raises:
|
85
|
+
ValueError: If the message is empty or None, or if the session_id is empty or None.
|
86
|
+
"""
|
87
|
+
if not message:
|
88
|
+
raise ValueError("Message cannot be empty or None")
|
89
|
+
if not session_id:
|
90
|
+
raise ValueError("Session ID cannot be empty or None")
|
91
|
+
|
92
|
+
return self.chat_repository.chat(message, session_id, chat_history)
|
93
|
+
|
94
|
+
def chat_with_documents(self, message: str, session_id: str, documents: List[Dict[str, str]],
|
95
|
+
chat_history: List[Dict[str, str]] = None) -> Optional[str]:
|
96
|
+
"""
|
97
|
+
Chat with the Granite model using provided documents as context.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
message: The user's message.
|
101
|
+
session_id: The ID of the chat session.
|
102
|
+
documents: List of documents to use as context. Each document should have 'id' and 'content' keys.
|
103
|
+
chat_history: Optional chat history to include in the conversation.
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
Optional[str]: The model's response, or None if an error occurs.
|
107
|
+
|
108
|
+
Raises:
|
109
|
+
ValueError: If the message is empty or None, or if the session_id is empty or None.
|
110
|
+
"""
|
111
|
+
if not message:
|
112
|
+
raise ValueError("Message cannot be empty or None")
|
113
|
+
if not session_id:
|
114
|
+
raise ValueError("Session ID cannot be empty or None")
|
115
|
+
if not documents:
|
116
|
+
raise ValueError("Documents list cannot be empty or None")
|
117
|
+
|
118
|
+
# Start with system prompts and user message
|
119
|
+
messages = []
|
120
|
+
|
121
|
+
# Add document messages
|
122
|
+
for doc in documents:
|
123
|
+
doc_id = doc.get('id', '')
|
124
|
+
content = doc.get('content', '')
|
125
|
+
if content:
|
126
|
+
# Use the document role with optional ID
|
127
|
+
role = f"document{doc_id}" if doc_id else "document"
|
128
|
+
messages.append(OllamaChatMessage(role, content))
|
129
|
+
|
130
|
+
# Add chat history if provided
|
131
|
+
if chat_history:
|
132
|
+
for msg in chat_history:
|
133
|
+
messages.append(OllamaChatMessage(msg.get("role", "user"), msg.get("content", "")))
|
134
|
+
|
135
|
+
# Add the current user message
|
136
|
+
messages.append(OllamaChatMessage("user", message))
|
137
|
+
|
138
|
+
# Call the chat repository with the prepared messages
|
139
|
+
return self.chat_repository.chat_with_messages(messages)
|
@@ -0,0 +1,117 @@
|
|
1
|
+
from typing import Dict, List, Optional
|
2
|
+
|
3
|
+
from .base_model_service import BaseModelService
|
4
|
+
from ...infrastructure.llm.ollama.factories.nomic_factory import NomicModelFactory
|
5
|
+
|
6
|
+
|
7
|
+
class NomicService(BaseModelService):
|
8
|
+
"""
|
9
|
+
Service for interacting with the Nomic model.
|
10
|
+
Provides specialized methods for the Nomic model, focusing on embeddings.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, api_url: str, api_token: str = None):
|
14
|
+
"""
|
15
|
+
Initialize a new NomicService.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
api_url: The base URL of the LLM API.
|
19
|
+
api_token: Optional API token for authentication.
|
20
|
+
|
21
|
+
Raises:
|
22
|
+
ValueError: If the api_url is empty or None.
|
23
|
+
"""
|
24
|
+
if not api_url:
|
25
|
+
raise ValueError("API URL cannot be empty or None")
|
26
|
+
|
27
|
+
self.api_url = api_url
|
28
|
+
self.api_token = api_token
|
29
|
+
|
30
|
+
# Create the Nomic model factory
|
31
|
+
self.factory = NomicModelFactory(
|
32
|
+
api_url=api_url,
|
33
|
+
api_token=api_token
|
34
|
+
)
|
35
|
+
|
36
|
+
# Create repositories
|
37
|
+
self.embedding_repository = self.factory.create_embedding_repository()
|
38
|
+
|
39
|
+
def generate(self, prompt: str, system_prompt: str = None, max_tokens: int = None) -> Optional[str]:
|
40
|
+
"""
|
41
|
+
Generate text using the Nomic model.
|
42
|
+
Nomic doesn't support text generation, so this always returns None.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
prompt: The prompt to generate text from.
|
46
|
+
system_prompt: Optional system prompt to use.
|
47
|
+
max_tokens: Optional maximum number of tokens to generate.
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
Optional[str]: Always None as Nomic doesn't support text generation.
|
51
|
+
"""
|
52
|
+
return None
|
53
|
+
|
54
|
+
def chat(self, message: str, session_id: str, chat_history: List[Dict[str, str]] = None) -> Optional[str]:
|
55
|
+
"""
|
56
|
+
Chat with the Nomic model.
|
57
|
+
Nomic doesn't support chat, so this always returns None.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
message: The user's message.
|
61
|
+
session_id: The ID of the chat session.
|
62
|
+
chat_history: Optional chat history to include in the conversation.
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
Optional[str]: Always None as Nomic doesn't support chat.
|
66
|
+
"""
|
67
|
+
return None
|
68
|
+
|
69
|
+
def embed(self, text: str) -> Optional[List[float]]:
|
70
|
+
"""
|
71
|
+
Create an embedding for the given text using the Nomic model.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
text: The text to create an embedding for.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
Optional[List[float]]: The embedding vector, or None if an error occurs.
|
78
|
+
|
79
|
+
Raises:
|
80
|
+
ValueError: If the text is empty or None.
|
81
|
+
"""
|
82
|
+
if not text:
|
83
|
+
raise ValueError("Text cannot be empty or None")
|
84
|
+
|
85
|
+
return self.embedding_repository.embed(text)
|
86
|
+
|
87
|
+
def batch_embed(self, texts: List[str]) -> Optional[List[List[float]]]:
|
88
|
+
"""
|
89
|
+
Create embeddings for multiple texts in a single batch.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
texts: List of texts to create embeddings for.
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
Optional[List[List[float]]]: List of embedding vectors, or None if an error occurs.
|
96
|
+
|
97
|
+
Raises:
|
98
|
+
ValueError: If the texts list is empty or None.
|
99
|
+
"""
|
100
|
+
if not texts:
|
101
|
+
raise ValueError("Texts list cannot be empty or None")
|
102
|
+
|
103
|
+
# Filter out empty texts
|
104
|
+
valid_texts = [text for text in texts if text]
|
105
|
+
if not valid_texts:
|
106
|
+
raise ValueError("All texts in the list are empty")
|
107
|
+
|
108
|
+
# Process each text individually
|
109
|
+
# In a real implementation, this could be optimized to use batch processing if supported by the API
|
110
|
+
embeddings = []
|
111
|
+
for text in valid_texts:
|
112
|
+
embedding = self.embedding_repository.embed(text)
|
113
|
+
if embedding is None:
|
114
|
+
return None # If any embedding fails, return None
|
115
|
+
embeddings.append(embedding)
|
116
|
+
|
117
|
+
return embeddings
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class LLMConfig:
|
2
|
+
"""
|
3
|
+
Configuration for an LLM service.
|
4
|
+
"""
|
5
|
+
def __init__(self, url: str, model_name: str, temperature: float = 0.6, max_tokens: int = 2500, api_token: str = None):
|
6
|
+
"""
|
7
|
+
Initialize a new LLMConfig.
|
8
|
+
|
9
|
+
Args:
|
10
|
+
url: The base URL of the LLM service.
|
11
|
+
model_name: The name of the model to use.
|
12
|
+
temperature: The temperature to use for generation (higher = more creative, lower = more deterministic).
|
13
|
+
max_tokens: The maximum number of tokens to generate.
|
14
|
+
api_token: Optional API token for authentication.
|
15
|
+
"""
|
16
|
+
self.url = url
|
17
|
+
self.model_name = model_name
|
18
|
+
self.temperature = temperature
|
19
|
+
self.max_tokens = max_tokens
|
20
|
+
self.api_token = api_token
|
@@ -0,0 +1,44 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from .reasoning.llm_chat_repository import LlmChatRepository
|
5
|
+
from .reasoning.llm_embedding_repository import LlmEmbeddingRepository
|
6
|
+
from .reasoning.llm_generate_respository import LlmGenerateRepository
|
7
|
+
|
8
|
+
|
9
|
+
class ModelFactory(ABC):
|
10
|
+
"""
|
11
|
+
Abstract factory for creating model-specific repositories.
|
12
|
+
This factory is responsible for creating the appropriate repositories for a given model.
|
13
|
+
"""
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
def create_chat_repository(self) -> LlmChatRepository:
|
17
|
+
"""
|
18
|
+
Creates a chat repository for the model.
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
LlmChatRepository: A repository for chat interactions with the model.
|
22
|
+
"""
|
23
|
+
pass
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
def create_generate_repository(self) -> LlmGenerateRepository:
|
27
|
+
"""
|
28
|
+
Creates a generate repository for the model.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
LlmGenerateRepository: A repository for text generation with the model.
|
32
|
+
"""
|
33
|
+
pass
|
34
|
+
|
35
|
+
@abstractmethod
|
36
|
+
def create_embedding_repository(self) -> Optional[LlmEmbeddingRepository]:
|
37
|
+
"""
|
38
|
+
Creates an embedding repository for the model.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
Optional[LlmEmbeddingRepository]: A repository for creating embeddings with the model,
|
42
|
+
or None if the model doesn't support embeddings.
|
43
|
+
"""
|
44
|
+
pass
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Optional, List, Dict, Any, Protocol
|
3
|
+
|
4
|
+
|
5
|
+
class ChatMessage(Protocol):
|
6
|
+
"""Protocol for chat messages."""
|
7
|
+
role: str
|
8
|
+
content: str
|
9
|
+
|
10
|
+
|
11
|
+
class LlmChatRepository(ABC):
|
12
|
+
"""
|
13
|
+
Repository for chat interactions with an LLM.
|
14
|
+
"""
|
15
|
+
|
16
|
+
@abstractmethod
|
17
|
+
def chat(self, user_message: str, session_id: str, chat_history: List[Dict[str, str]] = None) -> Optional[str]:
|
18
|
+
"""
|
19
|
+
Chat with the LLM.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
user_message: The user's message.
|
23
|
+
session_id: The ID of the chat session.
|
24
|
+
chat_history: Optional chat history to include in the conversation.
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
Optional[str]: The LLM's response, or None if the request failed.
|
28
|
+
"""
|
29
|
+
...
|
30
|
+
|
31
|
+
@abstractmethod
|
32
|
+
def chat_with_messages(self, messages: List[Any]) -> Optional[str]:
|
33
|
+
"""
|
34
|
+
Chat with the model using a custom list of messages.
|
35
|
+
|
36
|
+
This method allows for more flexibility in message formatting, such as including
|
37
|
+
document messages or other special message types.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
messages: The list of messages to send to the model. Each message should have
|
41
|
+
'role' and 'content' attributes or keys.
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
Optional[str]: The model's response, or None if the request failed.
|
45
|
+
"""
|
46
|
+
...
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import List, Optional
|
3
|
+
|
4
|
+
class LlmEmbeddingRepository(ABC):
|
5
|
+
"""
|
6
|
+
Repository for creating embeddings with an LLM.
|
7
|
+
"""
|
8
|
+
|
9
|
+
@abstractmethod
|
10
|
+
def embed(self, text: str) -> Optional[List[float]]:
|
11
|
+
"""
|
12
|
+
Create an embedding for the given text.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
text: The text to create an embedding for.
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
Optional[List[float]]: The embedding vector, or None if the request failed.
|
19
|
+
"""
|
20
|
+
pass
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
|
5
|
+
class LlmGenerateRepository(ABC):
|
6
|
+
"""
|
7
|
+
Repository for text generation with an LLM.
|
8
|
+
"""
|
9
|
+
|
10
|
+
@abstractmethod
|
11
|
+
def generate(self, user_prompt: str, system_prompt: str = None, max_tokens: int = None) -> Optional[str]:
|
12
|
+
"""
|
13
|
+
Generate text using the LLM.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
user_prompt: The prompt to generate text from.
|
17
|
+
system_prompt: Optional system prompt to use.
|
18
|
+
max_tokens: Optional maximum number of tokens to generate.
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
Optional[str]: The generated text, or None if the request failed.
|
22
|
+
"""
|
23
|
+
...
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
from ...domain.reasoning.tool_message import ToolMessage
|
5
|
+
|
6
|
+
|
7
|
+
class LlmToolRepository(ABC):
|
8
|
+
"""
|
9
|
+
Repository for tool interactions with an LLM.
|
10
|
+
"""
|
11
|
+
|
12
|
+
@abstractmethod
|
13
|
+
def find_tools_in_message(self, message: str) -> List[ToolMessage] | None:
|
14
|
+
"""
|
15
|
+
Extract tool calls from a message.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
message: The message to extract tool calls from.
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
List[ToolMessage] | None: A list of tool messages, or None if no tools were found.
|
22
|
+
"""
|
23
|
+
...
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
def build_tool_response_prompt(self, question: str, tool_results: list[str]) -> str | None:
|
27
|
+
"""
|
28
|
+
Build a prompt for the LLM to respond to tool results.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
question: The original question that triggered the tool calls.
|
32
|
+
tool_results: The results from executing the tools.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
str | None: The prompt for the LLM, or None if the prompt could not be built.
|
36
|
+
"""
|
37
|
+
...
|