tinyflow-llm 0.1.0__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.
Files changed (43) hide show
  1. tinyflow/__init__.py +53 -0
  2. tinyflow/config/helpers.py +62 -0
  3. tinyflow/config/settings.py +80 -0
  4. tinyflow/core/__init__.py +31 -0
  5. tinyflow/core/agent.py +457 -0
  6. tinyflow/core/exceptions.py +63 -0
  7. tinyflow/core/logger.py +13 -0
  8. tinyflow/core/message.py +6 -0
  9. tinyflow/core/protocol.py +81 -0
  10. tinyflow/core/tools.py +200 -0
  11. tinyflow/core/types.py +252 -0
  12. tinyflow/embeddings/__init__.py +4 -0
  13. tinyflow/embeddings/base.py +32 -0
  14. tinyflow/embeddings/factory.py +140 -0
  15. tinyflow/embeddings/local_embedding.py +65 -0
  16. tinyflow/embeddings/openai_embedding.py +70 -0
  17. tinyflow/memory/__init__.py +5 -0
  18. tinyflow/memory/base.py +16 -0
  19. tinyflow/memory/simple.py +34 -0
  20. tinyflow/memory/vector.py +26 -0
  21. tinyflow/providers/anthropic_llm.py +132 -0
  22. tinyflow/providers/base/factory.py +81 -0
  23. tinyflow/providers/base/llm.py +37 -0
  24. tinyflow/providers/gemini_llm.py +130 -0
  25. tinyflow/providers/openai_llm.py +198 -0
  26. tinyflow/tools/builtin/__init__.py +36 -0
  27. tinyflow/tools/builtin/code_execution.py +143 -0
  28. tinyflow/tools/builtin/search.py +145 -0
  29. tinyflow/tools/builtin/web_reader.py +88 -0
  30. tinyflow/vector/__init__.py +4 -0
  31. tinyflow/vector/base.py +65 -0
  32. tinyflow/vector/chroma_db.py +134 -0
  33. tinyflow/vector/factory.py +84 -0
  34. tinyflow/vector/qdrant_db.py +198 -0
  35. tinyflow/workflow/__init__.py +21 -0
  36. tinyflow/workflow/executor.py +272 -0
  37. tinyflow/workflow/hooks.py +191 -0
  38. tinyflow/workflow/state.py +148 -0
  39. tinyflow/workflow/step.py +74 -0
  40. tinyflow_llm-0.1.0.dist-info/METADATA +243 -0
  41. tinyflow_llm-0.1.0.dist-info/RECORD +43 -0
  42. tinyflow_llm-0.1.0.dist-info/WHEEL +4 -0
  43. tinyflow_llm-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,63 @@
1
+ """TinyFlow Exceptions
2
+
3
+ This module defines the public exception hierarchy for the TinyFlow framework.
4
+ Users can catch these exceptions to handle errors in a structured way.
5
+ """
6
+
7
+ from typing import Any, Optional
8
+
9
+
10
+ class TinyFlowError(Exception):
11
+ """Base class for all TinyFlow exceptions."""
12
+
13
+ def __init__(self, message: str):
14
+ super().__init__(message)
15
+ self.message = message
16
+
17
+
18
+ class ConfigurationError(TinyFlowError):
19
+ """Raised when configuration is missing or invalid."""
20
+
21
+ pass
22
+
23
+
24
+ class ProviderError(TinyFlowError):
25
+ """Raised when an LLM provider fails (API errors, rate limits, etc.)."""
26
+
27
+ def __init__(
28
+ self,
29
+ message: str,
30
+ provider: str,
31
+ status_code: Optional[int] = None,
32
+ raw_response: Optional[Any] = None,
33
+ ):
34
+ super().__init__(message)
35
+ self.provider = provider
36
+ self.status_code = status_code
37
+ self.raw_response = raw_response
38
+
39
+
40
+ class ToolError(TinyFlowError):
41
+ """Raised when a tool execution fails."""
42
+
43
+ def __init__(self, message: str, tool_name: str):
44
+ super().__init__(message)
45
+ self.tool_name = tool_name
46
+
47
+
48
+ class WorkflowError(TinyFlowError):
49
+ """Raised when workflow execution fails (dependencies, routing, etc.)."""
50
+
51
+ pass
52
+
53
+
54
+ class VectorError(TinyFlowError):
55
+ """Raised when vector database operations fail."""
56
+
57
+ pass
58
+
59
+
60
+ class MemoryError(TinyFlowError):
61
+ """Raised when memory operations fail."""
62
+
63
+ pass
@@ -0,0 +1,13 @@
1
+ import logging
2
+
3
+
4
+ def setup_logger():
5
+ """Setup the main logger for the library with a NullHandler to prevent 'No handler found' warnings."""
6
+ logger = logging.getLogger("tinyflow")
7
+ # Only add NullHandler if no handlers are present to avoid duplication if called multiple times
8
+ if not logger.handlers:
9
+ logger.addHandler(logging.NullHandler())
10
+ return logger
11
+
12
+
13
+ logger = setup_logger()
@@ -0,0 +1,6 @@
1
+ from tinyflow.core.types import Message
2
+
3
+
4
+ def convert_to_model_messages(dict_messages: list[dict]) -> list[Message]:
5
+ """Convert a list of dictionaries to a list of Message objects."""
6
+ return [Message(**msg) for msg in dict_messages]
@@ -0,0 +1,81 @@
1
+ import json
2
+ from typing import AsyncGenerator, Set
3
+
4
+ from tinyflow.core.types import (
5
+ DataUIPart,
6
+ ReasoningUIPart,
7
+ TextUIPart,
8
+ ToolUIPart,
9
+ UIMessage,
10
+ )
11
+
12
+
13
+ class JSONStreamAdapter:
14
+ """
15
+ Adapts a UIMessage stream to a structured JSON delta stream.
16
+ Each yield is a valid JSON string representing a specific part update.
17
+ """
18
+
19
+ def __init__(self):
20
+ self._last_lengths = {} # Track lengths per part type/id
21
+ self._emitted_states: Set[str] = set()
22
+
23
+ async def transform(self, message_stream: AsyncGenerator[UIMessage, None]) -> AsyncGenerator[str, None]:
24
+ async for message in message_stream:
25
+ if message.role != "assistant":
26
+ continue
27
+
28
+ for part in message.parts:
29
+ # Use a combination of type and optional ID for tracking
30
+ part_id = getattr(part, "tool_call_id", None) or getattr(part, "id", "default")
31
+ track_key = f"{part.type}:{part_id}"
32
+
33
+ # 1. Handle Text & Reasoning (Incremental Deltas)
34
+ if isinstance(part, (TextUIPart, ReasoningUIPart)):
35
+ current_text = part.text
36
+ last_len = self._last_lengths.get(track_key, 0)
37
+
38
+ if len(current_text) > last_len:
39
+ delta = current_text[last_len:]
40
+ yield json.dumps({
41
+ "type": f"{part.type}-delta",
42
+ "delta": delta,
43
+ "index": message.parts.index(part) # Help frontend locate the part
44
+ }) + "\n"
45
+ self._last_lengths[track_key] = len(current_text)
46
+
47
+ # 2. Handle Tool Calls (State-based Events)
48
+ elif isinstance(part, ToolUIPart):
49
+ state_key = f"tool:{part.tool_call_id}:{part.state}"
50
+ if state_key not in self._emitted_states:
51
+ payload = {
52
+ "type": "tool-call" if "input" in part.state.value else "tool-result",
53
+ "status": part.state.value,
54
+ "toolCallId": part.tool_call_id,
55
+ "toolName": part.tool_name,
56
+ }
57
+ if part.input is not None:
58
+ payload["input"] = part.input
59
+ if part.output is not None:
60
+ payload["output"] = part.output
61
+ if part.error_text:
62
+ payload["error"] = part.error_text
63
+
64
+ yield json.dumps(payload) + "\n"
65
+ self._emitted_states.add(state_key)
66
+
67
+ # 3. Handle Custom Data
68
+ elif isinstance(part, DataUIPart):
69
+ if track_key not in self._emitted_states:
70
+ yield json.dumps({
71
+ "type": "data",
72
+ "id": part.id,
73
+ "data": part.data
74
+ }) + "\n"
75
+ self._emitted_states.add(track_key)
76
+
77
+ async def to_json_stream(message_stream: AsyncGenerator[UIMessage, None]) -> AsyncGenerator[str, None]:
78
+ """Helper function for the most common DX: agent.stream() -> to_json_stream()"""
79
+ adapter = JSONStreamAdapter()
80
+ async for chunk in adapter.transform(message_stream):
81
+ yield chunk
tinyflow/core/tools.py ADDED
@@ -0,0 +1,200 @@
1
+ import inspect
2
+ import logging
3
+ from typing import Any, Callable, Dict, Optional, Type
4
+
5
+ from docstring_parser import parse
6
+ from pydantic import BaseModel, Field, create_model
7
+
8
+ from tinyflow.core.exceptions import ToolError
9
+
10
+ logger = logging.getLogger("tinyflow.core.tools")
11
+
12
+
13
+ class Tool:
14
+ """
15
+ Explicit declarative tool definition (Vercel AI SDK style).
16
+
17
+ This is the unified tool class of the framework, providing clear control:
18
+ - name: Tool name
19
+ - description: Detailed description (LLM will decide when to call based on this)
20
+ - parameters: Pydantic model defining parameter structure
21
+ - execute: Execution function, receiving the validated Pydantic model instance
22
+
23
+ Example:
24
+ ```python
25
+ class WeatherInput(BaseModel):
26
+ city: str = Field(description="City name")
27
+ unit: str = Field(default="celsius", description="Temperature unit")
28
+
29
+ weather_tool = Tool(
30
+ name="get_weather",
31
+ description="Get weather information for a specific city.",
32
+ parameters=WeatherInput,
33
+ execute=lambda args: f"{args.city} is sunny"
34
+ )
35
+ ```
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ name: str,
41
+ description: str,
42
+ parameters: Type[BaseModel],
43
+ execute: Callable[[Any], Any],
44
+ ):
45
+ self.name = name
46
+ self.description = description
47
+ self.parameters = parameters
48
+ self.execute_func = execute
49
+ self.args_schema = parameters
50
+ self.func = execute
51
+ logger.debug(f"Tool initialized: {self.name}")
52
+
53
+ async def execute(self, **kwargs) -> Any:
54
+ """Execute the tool with automatic parameter validation."""
55
+ try:
56
+ validated_args = self.parameters.model_validate(kwargs)
57
+
58
+ if inspect.iscoroutinefunction(self.execute_func):
59
+ result = await self.execute_func(validated_args)
60
+ else:
61
+ result = self.execute_func(validated_args)
62
+
63
+ # If result is an Agent or WorkflowExecutor, run it
64
+ # This enables "Agent as a Tool" pattern
65
+ if hasattr(result, "run"):
66
+ if inspect.iscoroutinefunction(result.run):
67
+ # For Agents/Workflows, we might want to return the result directly
68
+ # or handle the run execution here.
69
+ # Simpler approach: execute .run() and return output
70
+ # For WorkflowExecutor, run() returns dict of results
71
+ # For Agent, run() returns str
72
+ output = await result.run(**kwargs) # Pass args if compatible?
73
+ # Actually, usually the tool function prepares the agent/workflow
74
+ # and might run it with specific args derived from validated_args.
75
+ # If the tool function *returns* the runner instance, we run it.
76
+ # But usually the tool body does the running.
77
+ # Let's assume the tool body returns the FINAL result (str/dict).
78
+ # So no change needed here if tool body handles execution.
79
+
80
+ # BUT, if we want to support returning an AsyncGenerator (Streaming Workflow),
81
+ # we must NOT convert to str immediately.
82
+ pass
83
+
84
+ return result
85
+ except Exception as e:
86
+ if hasattr(e, "errors"):
87
+ logger.warning(f"Validation Error for tool '{self.name}': {str(e)}")
88
+ # Raise ToolError for validation failure?
89
+ # Or keep string return for LLM loop?
90
+ # The execute method is typically called by Agent which wants a string.
91
+ # However, for library users executing tools directly, an exception is better.
92
+ # Let's wrap it in ToolError but format it friendly.
93
+ raise ToolError(f"Validation Error: {str(e)}", tool_name=self.name) from e
94
+
95
+ logger.error(f"Error executing tool '{self.name}': {str(e)}")
96
+ raise ToolError(f"Execution Error: {str(e)}", tool_name=self.name) from e
97
+
98
+ def to_openai_schema(self, strict: bool = False) -> Dict[str, Any]:
99
+ """
100
+ Convert to OpenAI tool format.
101
+
102
+ Extract field descriptions from Pydantic model Field descriptions to ensure
103
+ LLM accurately understands the purpose of each parameter.
104
+ """
105
+ schema = self.parameters.model_json_schema()
106
+
107
+ parameters = {
108
+ "type": "object",
109
+ "properties": schema.get("properties", {}),
110
+ "required": schema.get("required", []),
111
+ }
112
+
113
+ if strict:
114
+ parameters["additionalProperties"] = False
115
+ parameters["required"] = list(schema.get("properties", {}).keys())
116
+
117
+ return {
118
+ "type": "function",
119
+ "function": {
120
+ "name": self.name,
121
+ "description": self.description,
122
+ "parameters": parameters,
123
+ "strict": strict,
124
+ },
125
+ }
126
+
127
+
128
+ def tool(
129
+ name: Optional[str] = None,
130
+ description: Optional[str] = None,
131
+ ):
132
+ """
133
+ Tool decorator supporting automatic tool definition derivation from functions.
134
+
135
+ Supports two modes:
136
+ 1. Decorating normal functions: Automatically parse description and parameter descriptions from docstring.
137
+ 2. Decorating class methods: Use with Tool class.
138
+
139
+ Example:
140
+ ```python
141
+ @tool()
142
+ async def get_weather(city: str):
143
+ '''Get weather.
144
+
145
+ Args:
146
+ city: City name
147
+ '''
148
+ return "Sunny"
149
+
150
+ @tool(name="custom_name", description="Custom description")
151
+ def my_tool(input: str):
152
+ return input
153
+ ```
154
+ """
155
+
156
+ def decorator(func: Callable) -> Tool:
157
+ tool_name = name or func.__name__
158
+
159
+ tool_description = description
160
+ if tool_description is None:
161
+ doc = parse(func.__doc__ or "")
162
+ tool_description = doc.short_description or f"Tool: {tool_name}"
163
+
164
+ # Create Pydantic model from function signature
165
+ args_schema = _get_args_schema(func)
166
+
167
+ # Create execute function that unwraps Pydantic model to kwargs
168
+ def execute_wrapper(args):
169
+ kwargs = args.model_dump()
170
+ if inspect.iscoroutinefunction(func):
171
+ # Return the coroutine - it will be awaited by Tool.execute
172
+ return func(**kwargs)
173
+ else:
174
+ return func(**kwargs)
175
+
176
+ return Tool(
177
+ name=tool_name,
178
+ description=tool_description,
179
+ parameters=args_schema,
180
+ execute=execute_wrapper,
181
+ )
182
+
183
+ return decorator
184
+
185
+
186
+ def _get_args_schema(func: Callable) -> Type[BaseModel]:
187
+ """Helper function to create Pydantic model from function signature."""
188
+ sig = inspect.signature(func)
189
+ doc = parse(func.__doc__ or "")
190
+
191
+ param_descriptions = {p.arg_name: p.description for p in doc.params}
192
+
193
+ fields = {}
194
+ for param_name, param in sig.parameters.items():
195
+ annotation = param.annotation if param.annotation != inspect.Parameter.empty else str
196
+ default = param.default if param.default != inspect.Parameter.empty else ...
197
+ description = param_descriptions.get(param_name, "")
198
+ fields[param_name] = (annotation, Field(default=default, description=description))
199
+
200
+ return create_model(f"{func.__name__}Schema", **fields)
tinyflow/core/types.py ADDED
@@ -0,0 +1,252 @@
1
+ from enum import Enum
2
+ from typing import Any, Dict, Generic, List, Literal, Optional, TypeVar, Union
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ # =============================================================================
7
+ # Legacy Types (Backward Compatibility)
8
+ # =============================================================================
9
+
10
+
11
+ class Message(BaseModel):
12
+ """Base message type for conversation history."""
13
+
14
+ role: Literal["system", "user", "assistant", "tool"]
15
+ content: Optional[str] = ""
16
+ name: Optional[str] = None
17
+ tool_calls: Optional[List[Dict[str, Any]]] = None
18
+ tool_call_id: Optional[str] = None
19
+ reasoning_content: Optional[str] = None
20
+
21
+
22
+ class StreamChunk(BaseModel):
23
+ """Legacy stream chunk - deprecated in favor of StreamPart types."""
24
+
25
+ content: str
26
+ finish_reason: Optional[str] = None
27
+
28
+
29
+ class LLMResponse(BaseModel):
30
+ """Response from LLM provider."""
31
+
32
+ content: Optional[str]
33
+ tool_calls: Optional[List[Dict[str, Any]]] = None
34
+ raw_response: Optional[object] = None
35
+ usage: Optional[dict] = None
36
+
37
+
38
+ # =============================================================================
39
+ # UI Message Part State Enums
40
+ # =============================================================================
41
+
42
+
43
+ class TextPartState(str, Enum):
44
+ """States for text content parts."""
45
+
46
+ STREAMING = "streaming"
47
+ DONE = "done"
48
+
49
+
50
+ class ToolPartState(str, Enum):
51
+ """States for tool execution lifecycle.
52
+
53
+ States:
54
+ INPUT_STREAMING: Tool arguments being generated by LLM
55
+ INPUT_AVAILABLE: Arguments complete, ready for execution
56
+ OUTPUT_AVAILABLE: Tool executed, result available
57
+ OUTPUT_ERROR: Tool execution failed
58
+ APPROVAL_REQUESTED: Waiting for user approval
59
+ """
60
+
61
+ INPUT_STREAMING = "input-streaming"
62
+ INPUT_AVAILABLE = "input-available"
63
+ OUTPUT_AVAILABLE = "output-available"
64
+ OUTPUT_ERROR = "output-error"
65
+ APPROVAL_REQUESTED = "approval-requested"
66
+
67
+
68
+ # =============================================================================
69
+ # UI Message Part Types
70
+ # =============================================================================
71
+
72
+
73
+ class TextUIPart(BaseModel):
74
+ """Text content with streaming state."""
75
+
76
+ type: Literal["text"] = "text"
77
+ text: str
78
+ state: Optional[TextPartState] = None
79
+
80
+
81
+ class ReasoningUIPart(BaseModel):
82
+ """Agent thinking/reasoning trace."""
83
+
84
+ type: Literal["reasoning"] = "reasoning"
85
+ text: str
86
+ state: Optional[TextPartState] = None
87
+ provider_metadata: Optional[Dict[str, Any]] = None
88
+
89
+
90
+ class ToolUIPart(BaseModel):
91
+ """Tool invocation and result representation.
92
+
93
+ This part represents the complete lifecycle of a tool call:
94
+ 1. INPUT_STREAMING: Tool arguments being generated by LLM
95
+ 2. INPUT_AVAILABLE: Arguments complete, ready for execution
96
+ 3. OUTPUT_AVAILABLE: Tool executed, result available
97
+ 4. OUTPUT_ERROR: Tool execution failed
98
+ 5. APPROVAL_REQUESTED: Waiting for user to approve execution
99
+ """
100
+
101
+ type: str = Field(..., description="Format: tool-{tool_name}")
102
+ tool_call_id: str
103
+ tool_name: str
104
+ state: ToolPartState
105
+
106
+ # Input data (when state is INPUT_* or OUTPUT_*)
107
+ input: Optional[Dict[str, Any]] = None
108
+
109
+ # Output data (when state is OUTPUT_AVAILABLE)
110
+ output: Optional[Any] = None
111
+
112
+ # Error text (when state is OUTPUT_ERROR)
113
+ error_text: Optional[str] = None
114
+
115
+ # Approval data (when state is APPROVAL_REQUESTED)
116
+ approval_id: Optional[str] = None
117
+
118
+ # Execution flag
119
+ provider_executed: Optional[bool] = None
120
+
121
+
122
+ class FileUIPart(BaseModel):
123
+ """File or image attachment."""
124
+
125
+ type: Literal["file"] = "file"
126
+ media_type: str = Field(..., description="IANA media type (e.g., image/png)")
127
+ filename: Optional[str] = None
128
+ url: str = Field(..., description="Can be URL or data URL (base64)")
129
+
130
+
131
+ class DataUIPart(BaseModel):
132
+ """Custom data part for structured data.
133
+
134
+ This allows passing arbitrary structured data to the frontend
135
+ for custom UI components.
136
+ """
137
+
138
+ type: str = Field(..., description="Format: data-{data_name}")
139
+ id: Optional[str] = None
140
+ data: Dict[str, Any]
141
+
142
+
143
+ class StepStartUIPart(BaseModel):
144
+ """Marker for step boundary in multi-step tool execution."""
145
+
146
+ type: Literal["step-start"] = "step-start"
147
+
148
+
149
+ # Union type for all UI message parts
150
+ UIMessagePart = Union[
151
+ TextUIPart,
152
+ ReasoningUIPart,
153
+ ToolUIPart,
154
+ FileUIPart,
155
+ DataUIPart,
156
+ StepStartUIPart,
157
+ ]
158
+
159
+
160
+ # =============================================================================
161
+ # UI Message Type
162
+ # =============================================================================
163
+
164
+ METADATA = TypeVar("METADATA", bound=BaseModel)
165
+ DATA_PARTS = TypeVar("DATA_PARTS", bound=BaseModel)
166
+ TOOLS = TypeVar("TOOLS", bound=BaseModel)
167
+
168
+
169
+ class UIMessage(BaseModel, Generic[METADATA, DATA_PARTS, TOOLS]):
170
+ """UI Message - Complete message representation for frontend rendering.
171
+
172
+ This is the single source of truth for application state, containing
173
+ full message history including metadata, data parts, and contextual
174
+ information for UI rendering.
175
+
176
+ Generic Parameters:
177
+ METADATA: Custom metadata type for additional message information
178
+ DATA_PARTS: Custom data part types for structured components
179
+ TOOLS: Tool definitions for type-safe tool interactions
180
+ """
181
+
182
+ id: str
183
+ role: Literal["system", "user", "assistant"]
184
+ metadata: Optional[METADATA] = None
185
+ parts: List[UIMessagePart]
186
+
187
+
188
+ # =============================================================================
189
+ # Stream Part Types (for Provider-Level Streaming)
190
+ # =============================================================================
191
+
192
+
193
+ class TextStreamDelta(BaseModel):
194
+ """Text delta in streaming response."""
195
+
196
+ type: Literal["text"] = "text"
197
+ text: str
198
+
199
+
200
+ class ReasoningStreamDelta(BaseModel):
201
+ """Reasoning delta in streaming response."""
202
+
203
+ type: Literal["reasoning"] = "reasoning"
204
+ text: str
205
+
206
+
207
+ class ToolCallStreamStart(BaseModel):
208
+ """Tool call streaming starts."""
209
+
210
+ type: Literal["tool-call-streaming-start"] = "tool-call-streaming-start"
211
+ tool_call_id: str
212
+ tool_name: str
213
+
214
+
215
+ class ToolCallStreamDelta(BaseModel):
216
+ """Tool call argument delta (streaming)."""
217
+
218
+ type: Literal["tool-call-delta"] = "tool-call-delta"
219
+ tool_call_id: str
220
+ tool_name: str
221
+ args_text_delta: str
222
+
223
+
224
+ class ToolCallComplete(BaseModel):
225
+ """Complete tool call (when streaming is off or complete)."""
226
+
227
+ type: Literal["tool-call"] = "tool-call"
228
+ tool_call_id: str
229
+ tool_name: str
230
+ input: Dict[str, Any]
231
+
232
+
233
+ class ToolResultStream(BaseModel):
234
+ """Tool execution result."""
235
+
236
+ type: Literal["tool-result"] = "tool-result"
237
+ tool_call_id: str
238
+ tool_name: str
239
+ input: Dict[str, Any]
240
+ output: Any
241
+ is_error: bool = False
242
+
243
+
244
+ # Union type for all stream parts
245
+ StreamPart = Union[
246
+ TextStreamDelta,
247
+ ReasoningStreamDelta,
248
+ ToolCallStreamStart,
249
+ ToolCallStreamDelta,
250
+ ToolCallComplete,
251
+ ToolResultStream,
252
+ ]
@@ -0,0 +1,4 @@
1
+ """Embedding package
2
+
3
+ Contains different embedding model implementations
4
+ """
@@ -0,0 +1,32 @@
1
+ """Embedding Abstraction Layer
2
+
3
+ Supports multiple embedding model providers
4
+ """
5
+
6
+ from abc import ABC, abstractmethod
7
+ from typing import List
8
+
9
+
10
+ class BaseEmbedding(ABC):
11
+ """Embedding abstract base class"""
12
+
13
+ @abstractmethod
14
+ async def embed(self, texts: List[str]) -> List[List[float]]:
15
+ """Convert text list to vectors
16
+
17
+ Args:
18
+ texts: List of text strings
19
+
20
+ Returns:
21
+ List of vectors (one for each text)
22
+ """
23
+ pass
24
+
25
+ @abstractmethod
26
+ def get_dimension(self) -> int:
27
+ """Get vector dimension
28
+
29
+ Returns:
30
+ Vector dimension
31
+ """
32
+ pass