flowllm 0.1.0__py3-none-any.whl → 0.1.1__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.
- flowllm/__init__.py +12 -0
- flowllm/app.py +25 -0
- flowllm/config/default_config.yaml +82 -0
- flowllm/config/pydantic_config_parser.py +242 -0
- flowllm/context/base_context.py +59 -0
- flowllm/context/flow_context.py +28 -0
- llmflow/op/prompt_mixin.py → flowllm/context/prompt_handler.py +25 -14
- flowllm/context/registry.py +26 -0
- flowllm/context/service_context.py +103 -0
- flowllm/embedding_model/__init__.py +1 -0
- {llmflow → flowllm}/embedding_model/base_embedding_model.py +2 -2
- {llmflow → flowllm}/embedding_model/openai_compatible_embedding_model.py +8 -8
- flowllm/flow_engine/__init__.py +1 -0
- flowllm/flow_engine/base_flow_engine.py +34 -0
- flowllm/flow_engine/simple_flow_engine.py +213 -0
- flowllm/llm/__init__.py +1 -0
- {llmflow → flowllm}/llm/base_llm.py +16 -24
- {llmflow → flowllm}/llm/openai_compatible_llm.py +64 -108
- flowllm/op/__init__.py +3 -0
- flowllm/op/akshare/get_ak_a_code_op.py +116 -0
- flowllm/op/akshare/get_ak_a_code_prompt.yaml +21 -0
- flowllm/op/akshare/get_ak_a_info_op.py +143 -0
- flowllm/op/base_op.py +169 -0
- flowllm/op/llm_base_op.py +63 -0
- flowllm/op/mock_op.py +42 -0
- flowllm/op/parallel_op.py +30 -0
- flowllm/op/sequential_op.py +29 -0
- flowllm/schema/flow_response.py +12 -0
- flowllm/schema/message.py +35 -0
- flowllm/schema/service_config.py +76 -0
- flowllm/schema/tool_call.py +110 -0
- flowllm/service/__init__.py +2 -0
- flowllm/service/base_service.py +59 -0
- flowllm/service/http_service.py +87 -0
- flowllm/service/mcp_service.py +45 -0
- flowllm/storage/__init__.py +1 -0
- flowllm/storage/vector_store/__init__.py +3 -0
- flowllm/storage/vector_store/base_vector_store.py +44 -0
- {llmflow → flowllm/storage}/vector_store/chroma_vector_store.py +11 -10
- {llmflow → flowllm/storage}/vector_store/es_vector_store.py +10 -9
- llmflow/vector_store/file_vector_store.py → flowllm/storage/vector_store/local_vector_store.py +110 -10
- flowllm/utils/common_utils.py +64 -0
- flowllm/utils/dataframe_cache.py +331 -0
- flowllm/utils/fetch_url.py +113 -0
- {llmflow → flowllm}/utils/timer.py +5 -4
- {flowllm-0.1.0.dist-info → flowllm-0.1.1.dist-info}/METADATA +31 -27
- flowllm-0.1.1.dist-info/RECORD +62 -0
- flowllm-0.1.1.dist-info/entry_points.txt +4 -0
- {flowllm-0.1.0.dist-info → flowllm-0.1.1.dist-info}/licenses/LICENSE +1 -1
- flowllm-0.1.1.dist-info/top_level.txt +1 -0
- flowllm-0.1.0.dist-info/RECORD +0 -66
- flowllm-0.1.0.dist-info/entry_points.txt +0 -3
- flowllm-0.1.0.dist-info/top_level.txt +0 -1
- llmflow/app.py +0 -53
- llmflow/config/config_parser.py +0 -80
- llmflow/config/mock_config.yaml +0 -58
- llmflow/embedding_model/__init__.py +0 -5
- llmflow/enumeration/agent_state.py +0 -8
- llmflow/llm/__init__.py +0 -5
- llmflow/mcp_server.py +0 -110
- llmflow/op/__init__.py +0 -10
- llmflow/op/base_op.py +0 -125
- llmflow/op/mock_op.py +0 -40
- llmflow/op/react/react_v1_op.py +0 -88
- llmflow/op/react/react_v1_prompt.yaml +0 -28
- llmflow/op/vector_store/__init__.py +0 -13
- llmflow/op/vector_store/recall_vector_store_op.py +0 -48
- llmflow/op/vector_store/update_vector_store_op.py +0 -28
- llmflow/op/vector_store/vector_store_action_op.py +0 -46
- llmflow/pipeline/pipeline.py +0 -94
- llmflow/pipeline/pipeline_context.py +0 -37
- llmflow/schema/app_config.py +0 -69
- llmflow/schema/experience.py +0 -144
- llmflow/schema/message.py +0 -68
- llmflow/schema/request.py +0 -32
- llmflow/schema/response.py +0 -29
- llmflow/service/__init__.py +0 -0
- llmflow/service/llmflow_service.py +0 -96
- llmflow/tool/__init__.py +0 -9
- llmflow/tool/base_tool.py +0 -80
- llmflow/tool/code_tool.py +0 -43
- llmflow/tool/dashscope_search_tool.py +0 -162
- llmflow/tool/mcp_tool.py +0 -77
- llmflow/tool/tavily_search_tool.py +0 -109
- llmflow/tool/terminate_tool.py +0 -23
- llmflow/utils/__init__.py +0 -0
- llmflow/utils/common_utils.py +0 -17
- llmflow/utils/file_handler.py +0 -25
- llmflow/utils/http_client.py +0 -156
- llmflow/utils/op_utils.py +0 -102
- llmflow/utils/registry.py +0 -33
- llmflow/vector_store/__init__.py +0 -7
- llmflow/vector_store/base_vector_store.py +0 -136
- {llmflow → flowllm/config}/__init__.py +0 -0
- {llmflow/config → flowllm/context}/__init__.py +0 -0
- {llmflow → flowllm}/enumeration/__init__.py +0 -0
- {llmflow → flowllm}/enumeration/chunk_enum.py +0 -0
- {llmflow → flowllm}/enumeration/http_enum.py +0 -0
- {llmflow → flowllm}/enumeration/role.py +0 -0
- {llmflow/op/react → flowllm/op/akshare}/__init__.py +0 -0
- {llmflow/pipeline → flowllm/schema}/__init__.py +0 -0
- {llmflow → flowllm}/schema/vector_node.py +0 -0
- {llmflow/schema → flowllm/utils}/__init__.py +0 -0
- {llmflow → flowllm}/utils/singleton.py +0 -0
- {flowllm-0.1.0.dist-info → flowllm-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
from typing import Dict
|
2
|
+
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
5
|
+
from flowllm.schema.tool_call import ToolCall
|
6
|
+
|
7
|
+
|
8
|
+
class MCPConfig(BaseModel):
|
9
|
+
transport: str = Field(default="", description="stdio/http/sse/streamable-http")
|
10
|
+
host: str = Field(default="0.0.0.0")
|
11
|
+
port: int = Field(default=8001)
|
12
|
+
|
13
|
+
|
14
|
+
class HttpConfig(BaseModel):
|
15
|
+
host: str = Field(default="0.0.0.0")
|
16
|
+
port: int = Field(default=8001)
|
17
|
+
timeout_keep_alive: int = Field(default=600)
|
18
|
+
limit_concurrency: int = Field(default=64)
|
19
|
+
|
20
|
+
|
21
|
+
class FlowConfig(ToolCall):
|
22
|
+
flow_content: str = Field(default="")
|
23
|
+
|
24
|
+
def set_name(self, name: str):
|
25
|
+
self.name = name
|
26
|
+
return self
|
27
|
+
|
28
|
+
|
29
|
+
class FlowEngineConfig(BaseModel):
|
30
|
+
backend: str = Field(default="")
|
31
|
+
params: dict = Field(default_factory=dict)
|
32
|
+
|
33
|
+
|
34
|
+
class OpConfig(BaseModel):
|
35
|
+
backend: str = Field(default="")
|
36
|
+
language: str = Field(default="")
|
37
|
+
raise_exception: bool = Field(default=True)
|
38
|
+
prompt_path: str = Field(default="")
|
39
|
+
llm: str = Field(default="default")
|
40
|
+
embedding_model: str = Field(default="default")
|
41
|
+
vector_store: str = Field(default="default")
|
42
|
+
params: dict = Field(default_factory=dict)
|
43
|
+
|
44
|
+
|
45
|
+
class LLMConfig(BaseModel):
|
46
|
+
backend: str = Field(default="")
|
47
|
+
model_name: str = Field(default="")
|
48
|
+
params: dict = Field(default_factory=dict)
|
49
|
+
|
50
|
+
|
51
|
+
class EmbeddingModelConfig(BaseModel):
|
52
|
+
backend: str = Field(default="")
|
53
|
+
model_name: str = Field(default="")
|
54
|
+
params: dict = Field(default_factory=dict)
|
55
|
+
|
56
|
+
|
57
|
+
class VectorStoreConfig(BaseModel):
|
58
|
+
backend: str = Field(default="")
|
59
|
+
embedding_model: str = Field(default="")
|
60
|
+
params: dict = Field(default_factory=dict)
|
61
|
+
|
62
|
+
|
63
|
+
class ServiceConfig(BaseModel):
|
64
|
+
backend: str = Field(default="")
|
65
|
+
language: str = Field(default="")
|
66
|
+
thread_pool_max_workers: int = Field(default=16)
|
67
|
+
ray_max_workers: int = Field(default=8)
|
68
|
+
|
69
|
+
mcp: MCPConfig = Field(default_factory=MCPConfig)
|
70
|
+
http: HttpConfig = Field(default_factory=HttpConfig)
|
71
|
+
flow_engine: FlowEngineConfig = Field(default_factory=FlowEngineConfig)
|
72
|
+
flow: Dict[str, FlowConfig] = Field(default_factory=dict)
|
73
|
+
op: Dict[str, OpConfig] = Field(default_factory=dict)
|
74
|
+
llm: Dict[str, LLMConfig] = Field(default_factory=dict)
|
75
|
+
embedding_model: Dict[str, EmbeddingModelConfig] = Field(default_factory=dict)
|
76
|
+
vector_store: Dict[str, VectorStoreConfig] = Field(default_factory=dict)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Dict
|
3
|
+
|
4
|
+
from pydantic import BaseModel, Field
|
5
|
+
|
6
|
+
|
7
|
+
class ParamAttrs(BaseModel):
|
8
|
+
type: str = Field(default="str", description="tool parameter type")
|
9
|
+
description: str = Field(default="", description="tool parameter description")
|
10
|
+
required: bool = Field(default=True, description="tool parameter required")
|
11
|
+
|
12
|
+
|
13
|
+
class ToolCall(BaseModel):
|
14
|
+
"""
|
15
|
+
input:
|
16
|
+
{
|
17
|
+
"type": "function",
|
18
|
+
"function": {
|
19
|
+
"name": "get_current_weather",
|
20
|
+
"description": "It is very useful when you want to check the weather of a specified city.",
|
21
|
+
"parameters": {
|
22
|
+
"type": "object",
|
23
|
+
"properties": {
|
24
|
+
"location": {
|
25
|
+
"type": "string",
|
26
|
+
"description": "Cities or counties, such as Beijing, Hangzhou, Yuhang District, etc.",
|
27
|
+
}
|
28
|
+
},
|
29
|
+
"required": ["location"]
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
output:
|
34
|
+
{
|
35
|
+
"index": 0
|
36
|
+
"id": "call_6596dafa2a6a46f7a217da",
|
37
|
+
"function": {
|
38
|
+
"arguments": "{\"location\": \"Beijing\"}",
|
39
|
+
"name": "get_current_weather"
|
40
|
+
},
|
41
|
+
"type": "function",
|
42
|
+
}
|
43
|
+
"""
|
44
|
+
|
45
|
+
index: int = Field(default=0)
|
46
|
+
id: str = Field(default="")
|
47
|
+
type: str = Field(default="function")
|
48
|
+
name: str = Field(default="")
|
49
|
+
|
50
|
+
arguments: dict = Field(default_factory=dict, description="tool execution arguments")
|
51
|
+
|
52
|
+
description: str = Field(default="")
|
53
|
+
input_schema: Dict[str, ParamAttrs] = Field(default_factory=dict)
|
54
|
+
output_schema: Dict[str, ParamAttrs] = Field(default_factory=dict)
|
55
|
+
|
56
|
+
def simple_input_dump(self, version: str = "v1") -> dict:
|
57
|
+
if version == "v1":
|
58
|
+
required_list = [name for name, tool_param in self.input_schema.items() if tool_param.required]
|
59
|
+
properties = {name: {
|
60
|
+
"type": tool_param.type,
|
61
|
+
"description": tool_param.description
|
62
|
+
} for name, tool_param in self.input_schema.items()}
|
63
|
+
|
64
|
+
return {
|
65
|
+
"type": self.type,
|
66
|
+
self.type: {
|
67
|
+
"name": self.name,
|
68
|
+
"description": self.description,
|
69
|
+
"parameters": {
|
70
|
+
"type": "object",
|
71
|
+
"properties": properties,
|
72
|
+
"required": required_list
|
73
|
+
},
|
74
|
+
},
|
75
|
+
}
|
76
|
+
|
77
|
+
else:
|
78
|
+
raise NotImplementedError(f"version {version} not supported")
|
79
|
+
|
80
|
+
def simple_output_dump(self, version: str = "v1") -> dict:
|
81
|
+
if version == "v1":
|
82
|
+
return {
|
83
|
+
"index": self.index,
|
84
|
+
"id": self.id,
|
85
|
+
self.type: {
|
86
|
+
"arguments": json.dumps(self.arguments, ensure_ascii=False),
|
87
|
+
"name": self.name
|
88
|
+
},
|
89
|
+
"type": self.type,
|
90
|
+
}
|
91
|
+
else:
|
92
|
+
raise NotImplementedError(f"version {version} not supported")
|
93
|
+
|
94
|
+
def update_by_output(self, data: dict, version: str = "v1"):
|
95
|
+
if version == "v1":
|
96
|
+
self.index = data.get("index", 0)
|
97
|
+
self.id = data.get("id", "")
|
98
|
+
tool_type = data.get("type", "")
|
99
|
+
tool_type_dict = data.get(tool_type, {})
|
100
|
+
if tool_type_dict:
|
101
|
+
name = tool_type_dict.get("name", "")
|
102
|
+
arguments = tool_type_dict.get("arguments", "")
|
103
|
+
if name:
|
104
|
+
self.name = name
|
105
|
+
if arguments:
|
106
|
+
self.arguments = json.loads(arguments)
|
107
|
+
else:
|
108
|
+
raise NotImplementedError(f"version {version} not supported")
|
109
|
+
|
110
|
+
return self
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor
|
2
|
+
from typing import Dict
|
3
|
+
|
4
|
+
from loguru import logger
|
5
|
+
|
6
|
+
from flowllm.context.flow_context import FlowContext
|
7
|
+
from flowllm.context.service_context import C
|
8
|
+
from flowllm.flow_engine.base_flow_engine import BaseFlowEngine
|
9
|
+
from flowllm.schema.flow_response import FlowResponse
|
10
|
+
from flowllm.schema.service_config import ServiceConfig, EmbeddingModelConfig, FlowConfig
|
11
|
+
|
12
|
+
|
13
|
+
class BaseService:
|
14
|
+
|
15
|
+
def __init__(self, service_config: ServiceConfig):
|
16
|
+
self.service_config = service_config
|
17
|
+
|
18
|
+
C.language = self.service_config.language
|
19
|
+
C.thread_pool = ThreadPoolExecutor(max_workers=self.service_config.thread_pool_max_workers)
|
20
|
+
for name, config in self.service_config.vector_store.items():
|
21
|
+
vector_store_cls = C.resolve_vector_store(config.backend)
|
22
|
+
embedding_model_config: EmbeddingModelConfig = self.service_config.embedding_model[config.embedding_model]
|
23
|
+
embedding_model_cls = C.resolve_embedding_model(embedding_model_config.backend)
|
24
|
+
embedding_model = embedding_model_cls(model_name=embedding_model_config.model_name,
|
25
|
+
**embedding_model_config.params)
|
26
|
+
C.set_vector_store(name, vector_store_cls(embedding_model=embedding_model, **config.params))
|
27
|
+
|
28
|
+
self.flow_engine_config = self.service_config.flow_engine
|
29
|
+
self.flow_engine_cls = C.resolve_flow_engine(self.flow_engine_config.backend)
|
30
|
+
self.flow_config_dict: Dict[str, FlowConfig] = \
|
31
|
+
{name: config.set_name(name) for name, config in self.service_config.flow.items()}
|
32
|
+
|
33
|
+
self.mcp_config = self.service_config.mcp
|
34
|
+
self.http_config = self.service_config.http
|
35
|
+
|
36
|
+
def execute_flow(self, flow_name: str, **kwargs) -> FlowResponse:
|
37
|
+
response = FlowResponse()
|
38
|
+
try:
|
39
|
+
logger.info(f"request.params={kwargs}")
|
40
|
+
flow_context = FlowContext(**kwargs,
|
41
|
+
response=response,
|
42
|
+
service_config=self.service_config.model_copy(deep=True))
|
43
|
+
|
44
|
+
flow_config = self.flow_config_dict[flow_name]
|
45
|
+
flow_engine: BaseFlowEngine = self.flow_engine_cls(flow_name=flow_name,
|
46
|
+
flow_content=flow_config.flow_content,
|
47
|
+
flow_context=flow_context,
|
48
|
+
**self.flow_engine_config.params)
|
49
|
+
flow_engine()
|
50
|
+
|
51
|
+
except Exception as e:
|
52
|
+
logger.exception(f"flow_name={flow_name} encounter error={e.args}")
|
53
|
+
response.success = False
|
54
|
+
response.answer = str(e.args)
|
55
|
+
|
56
|
+
return response
|
57
|
+
|
58
|
+
def __call__(self):
|
59
|
+
raise NotImplementedError
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import asyncio
|
2
|
+
from functools import partial
|
3
|
+
from typing import Dict, Optional
|
4
|
+
|
5
|
+
import uvicorn
|
6
|
+
from fastapi import FastAPI
|
7
|
+
from fastapi.middleware.cors import CORSMiddleware
|
8
|
+
from loguru import logger
|
9
|
+
from pydantic import BaseModel, create_model, Field
|
10
|
+
|
11
|
+
from flowllm.context.service_context import C
|
12
|
+
from flowllm.schema.flow_response import FlowResponse
|
13
|
+
from flowllm.schema.tool_call import ParamAttrs
|
14
|
+
from flowllm.service.base_service import BaseService
|
15
|
+
from flowllm.utils.common_utils import snake_to_camel
|
16
|
+
|
17
|
+
|
18
|
+
@C.register_service("http")
|
19
|
+
class HttpService(BaseService):
|
20
|
+
TYPE_MAPPING = {
|
21
|
+
"str": str,
|
22
|
+
"int": int,
|
23
|
+
"float": float,
|
24
|
+
"bool": bool
|
25
|
+
}
|
26
|
+
|
27
|
+
def __init__(self, *args, **kwargs):
|
28
|
+
super().__init__(*args, **kwargs)
|
29
|
+
self.app = FastAPI(title="FlowLLM", description="HTTP API for FlowLLM")
|
30
|
+
|
31
|
+
# Add CORS middleware
|
32
|
+
self.app.add_middleware(
|
33
|
+
CORSMiddleware,
|
34
|
+
allow_origins=["*"],
|
35
|
+
allow_credentials=True,
|
36
|
+
allow_methods=["*"],
|
37
|
+
allow_headers=["*"],
|
38
|
+
)
|
39
|
+
|
40
|
+
# Add health check endpoint
|
41
|
+
self.app.get("/health")(self.health_check)
|
42
|
+
|
43
|
+
@staticmethod
|
44
|
+
def health_check():
|
45
|
+
return {"status": "healthy"}
|
46
|
+
|
47
|
+
def _create_pydantic_model(self, flow_name: str, input_schema: Dict[str, ParamAttrs]) -> BaseModel:
|
48
|
+
# Create a dynamic Pydantic model based on flow input schema
|
49
|
+
fields = {}
|
50
|
+
|
51
|
+
for param_name, param_config in input_schema.items():
|
52
|
+
field_type = self.TYPE_MAPPING.get(param_config.type, str)
|
53
|
+
|
54
|
+
if not param_config.required:
|
55
|
+
fields[param_name] = (Optional[field_type], Field(default=None, description=param_config.description))
|
56
|
+
else:
|
57
|
+
fields[param_name] = (field_type, Field(default=..., description=param_config.description))
|
58
|
+
|
59
|
+
return create_model(f"{snake_to_camel(flow_name)}Model", **fields)
|
60
|
+
|
61
|
+
def register_flow(self, flow_name: str):
|
62
|
+
"""Register a flow as an HTTP endpoint"""
|
63
|
+
flow_config = self.flow_config_dict[flow_name]
|
64
|
+
request_model = self._create_pydantic_model(flow_name, flow_config.input_schema)
|
65
|
+
|
66
|
+
async def execute_flow_endpoint(request: request_model) -> FlowResponse:
|
67
|
+
loop = asyncio.get_event_loop()
|
68
|
+
response: FlowResponse = await loop.run_in_executor(
|
69
|
+
executor=C.thread_pool,
|
70
|
+
func=partial(self.execute_flow, flow_name=flow_name, **request.model_dump())) # noqa
|
71
|
+
|
72
|
+
return response
|
73
|
+
|
74
|
+
endpoint_path = f"/{flow_name}"
|
75
|
+
self.app.post(endpoint_path, response_model=FlowResponse)(execute_flow_endpoint)
|
76
|
+
logger.info(f"register flow={flow_name} endpoint={endpoint_path}")
|
77
|
+
|
78
|
+
def __call__(self):
|
79
|
+
for flow_name in self.flow_config_dict:
|
80
|
+
self.register_flow(flow_name)
|
81
|
+
|
82
|
+
# Start the server
|
83
|
+
uvicorn.run(self.app,
|
84
|
+
host=self.http_config.host,
|
85
|
+
port=self.http_config.port,
|
86
|
+
timeout_keep_alive=self.http_config.timeout_keep_alive,
|
87
|
+
limit_concurrency=self.http_config.limit_concurrency)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import asyncio
|
2
|
+
from functools import partial
|
3
|
+
|
4
|
+
from fastmcp import FastMCP
|
5
|
+
from fastmcp.tools import FunctionTool
|
6
|
+
from loguru import logger
|
7
|
+
|
8
|
+
from flowllm.context.service_context import C
|
9
|
+
from flowllm.service.base_service import BaseService
|
10
|
+
|
11
|
+
|
12
|
+
@C.register_service("mcp")
|
13
|
+
class MCPService(BaseService):
|
14
|
+
|
15
|
+
def __init__(self, *args, **kwargs):
|
16
|
+
super().__init__(*args, **kwargs)
|
17
|
+
self.mcp = FastMCP("FlowLLM")
|
18
|
+
|
19
|
+
def register_flow(self, flow_name: str):
|
20
|
+
flow_config = self.flow_config_dict[flow_name]
|
21
|
+
|
22
|
+
async def execute_flow_async(**kwargs) -> str:
|
23
|
+
loop = asyncio.get_event_loop()
|
24
|
+
response = await loop.run_in_executor(
|
25
|
+
executor=C.thread_pool,
|
26
|
+
func=partial(self.execute_flow, flow_name=flow_name, **kwargs)) # noqa
|
27
|
+
return response.answer
|
28
|
+
|
29
|
+
tool = FunctionTool(name=flow_name, # noqa
|
30
|
+
description=flow_config.description, # noqa
|
31
|
+
fn=execute_flow_async,
|
32
|
+
parameters=flow_config.input_schema)
|
33
|
+
self.mcp.add_tool(tool)
|
34
|
+
logger.info(f"register flow={flow_name}")
|
35
|
+
|
36
|
+
def __call__(self):
|
37
|
+
for flow_name in self.flow_config_dict:
|
38
|
+
self.register_flow(flow_name)
|
39
|
+
|
40
|
+
if self.mcp_config.transport == "sse":
|
41
|
+
self.mcp.run(transport="sse", host=self.mcp_config.host, port=self.mcp_config.port)
|
42
|
+
elif self.mcp_config.transport == "stdio":
|
43
|
+
self.mcp.run(transport="stdio")
|
44
|
+
else:
|
45
|
+
raise ValueError(f"unsupported mcp transport: {self.mcp_config.transport}")
|
@@ -0,0 +1 @@
|
|
1
|
+
from flowllm.storage import vector_store
|
@@ -0,0 +1,44 @@
|
|
1
|
+
from abc import ABC
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import List, Iterable
|
4
|
+
|
5
|
+
from pydantic import BaseModel, Field
|
6
|
+
|
7
|
+
from flowllm.embedding_model.base_embedding_model import BaseEmbeddingModel
|
8
|
+
from flowllm.schema.vector_node import VectorNode
|
9
|
+
|
10
|
+
|
11
|
+
class BaseVectorStore(BaseModel, ABC):
|
12
|
+
embedding_model: BaseEmbeddingModel | None = Field(default=None)
|
13
|
+
batch_size: int = Field(default=1024)
|
14
|
+
|
15
|
+
def exist_workspace(self, workspace_id: str, **kwargs) -> bool:
|
16
|
+
raise NotImplementedError
|
17
|
+
|
18
|
+
def delete_workspace(self, workspace_id: str, **kwargs):
|
19
|
+
raise NotImplementedError
|
20
|
+
|
21
|
+
def create_workspace(self, workspace_id: str, **kwargs):
|
22
|
+
raise NotImplementedError
|
23
|
+
|
24
|
+
def _iter_workspace_nodes(self, workspace_id: str, **kwargs) -> Iterable[VectorNode]:
|
25
|
+
raise NotImplementedError
|
26
|
+
|
27
|
+
def dump_workspace(self, workspace_id: str, path: str | Path = "", callback_fn=None, **kwargs):
|
28
|
+
raise NotImplementedError
|
29
|
+
|
30
|
+
def load_workspace(self, workspace_id: str, path: str | Path = "", nodes: List[VectorNode] = None, callback_fn=None,
|
31
|
+
**kwargs):
|
32
|
+
raise NotImplementedError
|
33
|
+
|
34
|
+
def copy_workspace(self, src_workspace_id: str, dest_workspace_id: str, **kwargs):
|
35
|
+
raise NotImplementedError
|
36
|
+
|
37
|
+
def search(self, query: str, workspace_id: str, top_k: int = 1, **kwargs) -> List[VectorNode]:
|
38
|
+
raise NotImplementedError
|
39
|
+
|
40
|
+
def insert(self, nodes: VectorNode | List[VectorNode], workspace_id: str, **kwargs):
|
41
|
+
raise NotImplementedError
|
42
|
+
|
43
|
+
def delete(self, node_ids: str | List[str], workspace_id: str, **kwargs):
|
44
|
+
raise NotImplementedError
|
@@ -6,17 +6,16 @@ from chromadb.config import Settings
|
|
6
6
|
from loguru import logger
|
7
7
|
from pydantic import Field, PrivateAttr, model_validator
|
8
8
|
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from
|
12
|
-
from llmflow.vector_store.base_vector_store import BaseVectorStore
|
9
|
+
from flowllm.context.service_context import C
|
10
|
+
from flowllm.schema.vector_node import VectorNode
|
11
|
+
from flowllm.storage.vector_store.local_vector_store import LocalVectorStore
|
13
12
|
|
14
13
|
|
15
|
-
@
|
16
|
-
class ChromaVectorStore(
|
14
|
+
@C.register_vector_store("chroma")
|
15
|
+
class ChromaVectorStore(LocalVectorStore):
|
17
16
|
store_dir: str = Field(default="./chroma_vector_store")
|
18
17
|
collections: dict = Field(default_factory=dict)
|
19
|
-
_client: chromadb.
|
18
|
+
_client: chromadb.ClientAPI = PrivateAttr()
|
20
19
|
|
21
20
|
@model_validator(mode="after")
|
22
21
|
def init_client(self):
|
@@ -97,8 +96,10 @@ class ChromaVectorStore(BaseVectorStore):
|
|
97
96
|
|
98
97
|
|
99
98
|
def main():
|
100
|
-
from
|
101
|
-
|
99
|
+
from flowllm.utils.common_utils import load_env
|
100
|
+
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
|
101
|
+
|
102
|
+
load_env()
|
102
103
|
|
103
104
|
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=64, model_name="text-embedding-v4")
|
104
105
|
workspace_id = "chroma_test_index"
|
@@ -185,4 +186,4 @@ def main():
|
|
185
186
|
|
186
187
|
if __name__ == "__main__":
|
187
188
|
main()
|
188
|
-
# launch with: python -m
|
189
|
+
# launch with: python -m flowllm.storage.chroma_vector_store
|
@@ -6,14 +6,13 @@ from elasticsearch.helpers import bulk
|
|
6
6
|
from loguru import logger
|
7
7
|
from pydantic import Field, PrivateAttr, model_validator
|
8
8
|
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from
|
12
|
-
from llmflow.vector_store.base_vector_store import BaseVectorStore
|
9
|
+
from flowllm.context.service_context import C
|
10
|
+
from flowllm.schema.vector_node import VectorNode
|
11
|
+
from flowllm.storage.vector_store.local_vector_store import LocalVectorStore
|
13
12
|
|
14
13
|
|
15
|
-
@
|
16
|
-
class EsVectorStore(
|
14
|
+
@C.register_vector_store("elasticsearch")
|
15
|
+
class EsVectorStore(LocalVectorStore):
|
17
16
|
hosts: str | List[str] = Field(default_factory=lambda: os.getenv("ES_HOSTS", "http://localhost:9200"))
|
18
17
|
basic_auth: str | Tuple[str, str] | None = Field(default=None)
|
19
18
|
retrieve_filters: List[dict] = []
|
@@ -24,6 +23,7 @@ class EsVectorStore(BaseVectorStore):
|
|
24
23
|
if isinstance(self.hosts, str):
|
25
24
|
self.hosts = [self.hosts]
|
26
25
|
self._client = Elasticsearch(hosts=self.hosts, basic_auth=self.basic_auth)
|
26
|
+
logger.info(f"Elasticsearch client initialized with hosts: {self.hosts}")
|
27
27
|
return self
|
28
28
|
|
29
29
|
def exist_workspace(self, workspace_id: str, **kwargs) -> bool:
|
@@ -162,8 +162,10 @@ class EsVectorStore(BaseVectorStore):
|
|
162
162
|
|
163
163
|
|
164
164
|
def main():
|
165
|
-
from
|
166
|
-
|
165
|
+
from flowllm.utils.common_utils import load_env
|
166
|
+
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
|
167
|
+
|
168
|
+
load_env()
|
167
169
|
|
168
170
|
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=64, model_name="text-embedding-v4")
|
169
171
|
workspace_id = "rag_nodes_index"
|
@@ -224,4 +226,3 @@ def main():
|
|
224
226
|
|
225
227
|
if __name__ == "__main__":
|
226
228
|
main()
|
227
|
-
# launch with: python -m llmflow.storage.es_vector_store
|