flowllm 0.1.0__py3-none-any.whl → 0.1.2__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 +21 -0
- flowllm/app.py +15 -0
- flowllm/client/__init__.py +25 -0
- flowllm/client/async_http_client.py +81 -0
- flowllm/client/http_client.py +81 -0
- flowllm/client/mcp_client.py +133 -0
- flowllm/client/sync_mcp_client.py +116 -0
- flowllm/config/__init__.py +1 -0
- flowllm/config/default.yaml +77 -0
- flowllm/config/empty.yaml +37 -0
- flowllm/config/pydantic_config_parser.py +242 -0
- flowllm/context/base_context.py +79 -0
- flowllm/context/flow_context.py +16 -0
- llmflow/op/prompt_mixin.py → flowllm/context/prompt_handler.py +25 -14
- flowllm/context/registry.py +30 -0
- flowllm/context/service_context.py +147 -0
- flowllm/embedding_model/__init__.py +1 -0
- {llmflow → flowllm}/embedding_model/base_embedding_model.py +93 -2
- {llmflow → flowllm}/embedding_model/openai_compatible_embedding_model.py +71 -13
- flowllm/flow/__init__.py +1 -0
- flowllm/flow/base_flow.py +72 -0
- flowllm/flow/base_tool_flow.py +15 -0
- flowllm/flow/gallery/__init__.py +8 -0
- flowllm/flow/gallery/cmd_flow.py +11 -0
- flowllm/flow/gallery/code_tool_flow.py +30 -0
- flowllm/flow/gallery/dashscope_search_tool_flow.py +34 -0
- flowllm/flow/gallery/deepsearch_tool_flow.py +39 -0
- flowllm/flow/gallery/expression_tool_flow.py +18 -0
- flowllm/flow/gallery/mock_tool_flow.py +67 -0
- flowllm/flow/gallery/tavily_search_tool_flow.py +30 -0
- flowllm/flow/gallery/terminate_tool_flow.py +30 -0
- flowllm/flow/parser/expression_parser.py +171 -0
- flowllm/llm/__init__.py +2 -0
- {llmflow → flowllm}/llm/base_llm.py +100 -18
- flowllm/llm/litellm_llm.py +455 -0
- flowllm/llm/openai_compatible_llm.py +439 -0
- flowllm/op/__init__.py +11 -0
- llmflow/op/react/react_v1_op.py → flowllm/op/agent/react_op.py +17 -22
- flowllm/op/akshare/__init__.py +3 -0
- flowllm/op/akshare/get_ak_a_code_op.py +108 -0
- flowllm/op/akshare/get_ak_a_code_prompt.yaml +21 -0
- flowllm/op/akshare/get_ak_a_info_op.py +140 -0
- flowllm/op/base_llm_op.py +64 -0
- flowllm/op/base_op.py +148 -0
- flowllm/op/base_ray_op.py +313 -0
- flowllm/op/code/__init__.py +1 -0
- flowllm/op/code/execute_code_op.py +42 -0
- flowllm/op/gallery/__init__.py +2 -0
- flowllm/op/gallery/mock_op.py +42 -0
- flowllm/op/gallery/terminate_op.py +29 -0
- flowllm/op/parallel_op.py +23 -0
- flowllm/op/search/__init__.py +3 -0
- flowllm/op/search/dashscope_deep_research_op.py +260 -0
- flowllm/op/search/dashscope_search_op.py +179 -0
- flowllm/op/search/dashscope_search_prompt.yaml +13 -0
- flowllm/op/search/tavily_search_op.py +102 -0
- flowllm/op/sequential_op.py +21 -0
- flowllm/schema/flow_request.py +12 -0
- flowllm/schema/flow_response.py +12 -0
- flowllm/schema/message.py +35 -0
- flowllm/schema/service_config.py +72 -0
- flowllm/schema/tool_call.py +118 -0
- {llmflow → flowllm}/schema/vector_node.py +1 -0
- flowllm/service/__init__.py +3 -0
- flowllm/service/base_service.py +68 -0
- flowllm/service/cmd_service.py +15 -0
- flowllm/service/http_service.py +79 -0
- flowllm/service/mcp_service.py +47 -0
- flowllm/storage/__init__.py +1 -0
- flowllm/storage/cache/__init__.py +1 -0
- flowllm/storage/cache/cache_data_handler.py +104 -0
- flowllm/storage/cache/data_cache.py +375 -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 +11 -11
- llmflow/vector_store/file_vector_store.py → flowllm/storage/vector_store/local_vector_store.py +110 -11
- flowllm/utils/common_utils.py +52 -0
- flowllm/utils/fetch_url.py +117 -0
- flowllm/utils/llm_utils.py +28 -0
- flowllm/utils/ridge_v2.py +54 -0
- {llmflow → flowllm}/utils/timer.py +5 -4
- {flowllm-0.1.0.dist-info → flowllm-0.1.2.dist-info}/METADATA +45 -388
- flowllm-0.1.2.dist-info/RECORD +99 -0
- flowllm-0.1.2.dist-info/entry_points.txt +2 -0
- {flowllm-0.1.0.dist-info → flowllm-0.1.2.dist-info}/licenses/LICENSE +1 -1
- flowllm-0.1.2.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/llm/openai_compatible_llm.py +0 -283
- 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/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/context}/__init__.py +0 -0
- {llmflow/config → 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/enumeration → flowllm/flow/parser}/__init__.py +0 -0
- {llmflow/op/react → flowllm/op/agent}/__init__.py +0 -0
- /llmflow/op/react/react_v1_prompt.yaml → /flowllm/op/agent/react_prompt.yaml +0 -0
- {llmflow/pipeline → flowllm/schema}/__init__.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.2.dist-info}/WHEEL +0 -0
llmflow/op/base_op.py
DELETED
@@ -1,125 +0,0 @@
|
|
1
|
-
from abc import abstractmethod, ABC
|
2
|
-
from concurrent.futures import Future
|
3
|
-
from pathlib import Path
|
4
|
-
from typing import List
|
5
|
-
|
6
|
-
from loguru import logger
|
7
|
-
from tqdm import tqdm
|
8
|
-
|
9
|
-
from llmflow.embedding_model import EMBEDDING_MODEL_REGISTRY
|
10
|
-
from llmflow.embedding_model.base_embedding_model import BaseEmbeddingModel
|
11
|
-
from llmflow.llm import LLM_REGISTRY
|
12
|
-
from llmflow.llm.base_llm import BaseLLM
|
13
|
-
from llmflow.op.prompt_mixin import PromptMixin
|
14
|
-
from llmflow.pipeline.pipeline_context import PipelineContext
|
15
|
-
from llmflow.schema.app_config import OpConfig, LLMConfig, EmbeddingModelConfig
|
16
|
-
from llmflow.utils.common_utils import camel_to_snake
|
17
|
-
from llmflow.utils.timer import Timer
|
18
|
-
from llmflow.vector_store.base_vector_store import BaseVectorStore
|
19
|
-
|
20
|
-
|
21
|
-
class BaseOp(PromptMixin, ABC):
|
22
|
-
current_path: str = __file__
|
23
|
-
|
24
|
-
def __init__(self, context: PipelineContext, op_config: OpConfig):
|
25
|
-
super().__init__()
|
26
|
-
self.context: PipelineContext = context
|
27
|
-
self.op_config: OpConfig = op_config
|
28
|
-
self.timer = Timer(name=self.simple_name)
|
29
|
-
|
30
|
-
self._prepare_prompt()
|
31
|
-
|
32
|
-
self._llm: BaseLLM | None = None
|
33
|
-
self._embedding_model: BaseEmbeddingModel | None = None
|
34
|
-
self._vector_store: BaseVectorStore | None = None
|
35
|
-
|
36
|
-
self.task_list: List[Future] = []
|
37
|
-
|
38
|
-
def _prepare_prompt(self):
|
39
|
-
if self.op_config.prompt_file_path:
|
40
|
-
prompt_file_path = self.op_config.prompt_file_path
|
41
|
-
else:
|
42
|
-
prompt_name = self.simple_name.replace("_op", "_prompt.yaml")
|
43
|
-
prompt_file_path = Path(self.current_path).parent / prompt_name
|
44
|
-
|
45
|
-
# Load custom prompts from prompt file
|
46
|
-
self.load_prompt_by_file(prompt_file_path=prompt_file_path)
|
47
|
-
|
48
|
-
# Load custom prompts from config
|
49
|
-
self.load_prompt_dict(prompt_dict=self.op_config.prompt_dict)
|
50
|
-
|
51
|
-
@property
|
52
|
-
def simple_name(self) -> str:
|
53
|
-
return camel_to_snake(self.__class__.__name__)
|
54
|
-
|
55
|
-
@property
|
56
|
-
def op_params(self) -> dict:
|
57
|
-
return self.op_config.params
|
58
|
-
|
59
|
-
@abstractmethod
|
60
|
-
def execute(self):
|
61
|
-
...
|
62
|
-
|
63
|
-
def execute_wrap(self):
|
64
|
-
try:
|
65
|
-
with self.timer:
|
66
|
-
return self.execute()
|
67
|
-
|
68
|
-
except Exception as e:
|
69
|
-
logger.exception(f"op={self.simple_name} execute failed, error={e.args}")
|
70
|
-
|
71
|
-
def submit_task(self, fn, *args, **kwargs):
|
72
|
-
task = self.context.thread_pool.submit(fn, *args, **kwargs)
|
73
|
-
self.task_list.append(task)
|
74
|
-
return self
|
75
|
-
|
76
|
-
def join_task(self, task_desc: str = None) -> list:
|
77
|
-
result = []
|
78
|
-
for task in tqdm(self.task_list, desc=task_desc or (self.simple_name + ".join_task")):
|
79
|
-
t_result = task.result()
|
80
|
-
if t_result:
|
81
|
-
if isinstance(t_result, list):
|
82
|
-
result.extend(t_result)
|
83
|
-
else:
|
84
|
-
result.append(t_result)
|
85
|
-
self.task_list.clear()
|
86
|
-
return result
|
87
|
-
|
88
|
-
@property
|
89
|
-
def llm(self) -> BaseLLM:
|
90
|
-
if self._llm is None:
|
91
|
-
llm_name: str = self.op_config.llm
|
92
|
-
assert llm_name in self.context.app_config.llm, f"llm={llm_name} not found in app_config.llm!"
|
93
|
-
llm_config: LLMConfig = self.context.app_config.llm[llm_name]
|
94
|
-
|
95
|
-
assert llm_config.backend in LLM_REGISTRY, f"llm.backend={llm_config.backend} not found in LLM_REGISTRY!"
|
96
|
-
llm_cls = LLM_REGISTRY[llm_config.backend]
|
97
|
-
self._llm = llm_cls(model_name=llm_config.model_name, **llm_config.params)
|
98
|
-
|
99
|
-
return self._llm
|
100
|
-
|
101
|
-
@property
|
102
|
-
def embedding_model(self):
|
103
|
-
if self._embedding_model is None:
|
104
|
-
embedding_model_name: str = self.op_config.embedding_model
|
105
|
-
assert embedding_model_name in self.context.app_config.embedding_model, \
|
106
|
-
f"embedding_model={embedding_model_name} not found in app_config.embedding_model!"
|
107
|
-
embedding_model_config: EmbeddingModelConfig = self.context.app_config.embedding_model[embedding_model_name]
|
108
|
-
|
109
|
-
assert embedding_model_config.backend in EMBEDDING_MODEL_REGISTRY, \
|
110
|
-
f"embedding_model.backend={embedding_model_config.backend} not found in EMBEDDING_MODEL_REGISTRY!"
|
111
|
-
embedding_model_cls = EMBEDDING_MODEL_REGISTRY[embedding_model_config.backend]
|
112
|
-
self._embedding_model = embedding_model_cls(model_name=embedding_model_config.model_name,
|
113
|
-
**embedding_model_config.params)
|
114
|
-
|
115
|
-
return self._embedding_model
|
116
|
-
|
117
|
-
@property
|
118
|
-
def vector_store(self):
|
119
|
-
if self._vector_store is None:
|
120
|
-
vector_store_name: str = self.op_config.vector_store
|
121
|
-
assert vector_store_name in self.context.vector_store_dict, \
|
122
|
-
f"vector_store={vector_store_name} not found in vector_store_dict!"
|
123
|
-
self._vector_store = self.context.vector_store_dict[vector_store_name]
|
124
|
-
|
125
|
-
return self._vector_store
|
llmflow/op/mock_op.py
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
import time
|
2
|
-
|
3
|
-
from loguru import logger
|
4
|
-
|
5
|
-
from llmflow.op import OP_REGISTRY
|
6
|
-
from llmflow.op.base_op import BaseOp
|
7
|
-
|
8
|
-
|
9
|
-
@OP_REGISTRY.register()
|
10
|
-
class Mock1Op(BaseOp):
|
11
|
-
def execute(self):
|
12
|
-
time.sleep(1)
|
13
|
-
a: int = self.op_params["a"]
|
14
|
-
b: str = self.op_params["b"]
|
15
|
-
logger.info(f"enter class={self.simple_name}. a={a} b={b}")
|
16
|
-
|
17
|
-
|
18
|
-
@OP_REGISTRY.register()
|
19
|
-
class Mock2Op(Mock1Op):
|
20
|
-
...
|
21
|
-
|
22
|
-
|
23
|
-
@OP_REGISTRY.register()
|
24
|
-
class Mock3Op(Mock1Op):
|
25
|
-
...
|
26
|
-
|
27
|
-
|
28
|
-
@OP_REGISTRY.register()
|
29
|
-
class Mock4Op(Mock1Op):
|
30
|
-
...
|
31
|
-
|
32
|
-
|
33
|
-
@OP_REGISTRY.register()
|
34
|
-
class Mock5Op(Mock1Op):
|
35
|
-
...
|
36
|
-
|
37
|
-
|
38
|
-
@OP_REGISTRY.register()
|
39
|
-
class Mock6Op(Mock1Op):
|
40
|
-
...
|
@@ -1,13 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
1. retrieve:
|
3
|
-
search: query(context), workspace_id(request), top_k(request)
|
4
|
-
2. summary:
|
5
|
-
insert: nodes(context), workspace_id(request)
|
6
|
-
delete: ids(context), workspace_id(request)
|
7
|
-
search: query(context), workspace_id(request), top_k(request.config.op)
|
8
|
-
3. vector:
|
9
|
-
dump: workspace_id(request), path(str), max_size(int)
|
10
|
-
load: workspace_id(request), path(str)
|
11
|
-
delete: workspace_id(request)
|
12
|
-
copy: source_id, target_id, max_size(int)
|
13
|
-
"""
|
@@ -1,48 +0,0 @@
|
|
1
|
-
from typing import List
|
2
|
-
|
3
|
-
from loguru import logger
|
4
|
-
|
5
|
-
from llmflow.op import OP_REGISTRY
|
6
|
-
from llmflow.op.base_op import BaseOp
|
7
|
-
from llmflow.schema.experience import BaseExperience, vector_node_to_experience
|
8
|
-
from llmflow.schema.request import RetrieverRequest
|
9
|
-
from llmflow.schema.response import RetrieverResponse
|
10
|
-
from llmflow.schema.vector_node import VectorNode
|
11
|
-
|
12
|
-
|
13
|
-
@OP_REGISTRY.register()
|
14
|
-
class RecallVectorStoreOp(BaseOp):
|
15
|
-
SEARCH_QUERY = "search_query"
|
16
|
-
SEARCH_MESSAGE = "search_message"
|
17
|
-
|
18
|
-
def execute(self):
|
19
|
-
# get query
|
20
|
-
query = self.context.get_context(self.SEARCH_QUERY)
|
21
|
-
assert query, "query should be not empty!"
|
22
|
-
|
23
|
-
# retrieve from vector store
|
24
|
-
request: RetrieverRequest = self.context.request
|
25
|
-
nodes: List[VectorNode] = self.vector_store.search(query=query,
|
26
|
-
workspace_id=request.workspace_id,
|
27
|
-
top_k=request.top_k)
|
28
|
-
|
29
|
-
# convert to experience, filter duplicate
|
30
|
-
experience_list: List[BaseExperience] = []
|
31
|
-
experience_content_list: List[str] = []
|
32
|
-
for node in nodes:
|
33
|
-
experience: BaseExperience = vector_node_to_experience(node)
|
34
|
-
if experience.content not in experience_content_list:
|
35
|
-
experience_list.append(experience)
|
36
|
-
experience_content_list.append(experience.content)
|
37
|
-
experience_size = len(experience_list)
|
38
|
-
logger.info(f"retrieve experience size={experience_size}")
|
39
|
-
|
40
|
-
# filter by score
|
41
|
-
threshold_score: float | None = self.op_params.get("threshold_score", None)
|
42
|
-
if threshold_score is not None:
|
43
|
-
experience_list = [e for e in experience_list if e.score >= threshold_score or e.score is None]
|
44
|
-
logger.info(f"after filter by threshold_score size={len(experience_list)}")
|
45
|
-
|
46
|
-
# set response
|
47
|
-
response: RetrieverResponse = self.context.response
|
48
|
-
response.experience_list = experience_list
|
@@ -1,28 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from typing import List
|
3
|
-
|
4
|
-
from loguru import logger
|
5
|
-
|
6
|
-
from llmflow.op import OP_REGISTRY
|
7
|
-
from llmflow.op.base_op import BaseOp
|
8
|
-
from llmflow.schema.experience import BaseExperience
|
9
|
-
from llmflow.schema.request import BaseRequest
|
10
|
-
from llmflow.schema.vector_node import VectorNode
|
11
|
-
|
12
|
-
|
13
|
-
@OP_REGISTRY.register()
|
14
|
-
class UpdateVectorStoreOp(BaseOp):
|
15
|
-
|
16
|
-
def execute(self):
|
17
|
-
request: BaseRequest = self.context.request
|
18
|
-
|
19
|
-
experience_ids: List[str] | None = self.context.response.deleted_experience_ids
|
20
|
-
if experience_ids:
|
21
|
-
self.vector_store.delete(node_ids=experience_ids, workspace_id=request.workspace_id)
|
22
|
-
logger.info(f"delete experience_ids={json.dumps(experience_ids, indent=2)}")
|
23
|
-
|
24
|
-
insert_experience_list: List[BaseExperience] | None = self.context.response.experience_list
|
25
|
-
if insert_experience_list:
|
26
|
-
insert_nodes: List[VectorNode] = [x.to_vector_node() for x in insert_experience_list]
|
27
|
-
self.vector_store.insert(nodes=insert_nodes, workspace_id=request.workspace_id)
|
28
|
-
logger.info(f"insert insert_node.size={len(insert_nodes)}")
|
@@ -1,46 +0,0 @@
|
|
1
|
-
from llmflow.op import OP_REGISTRY
|
2
|
-
from llmflow.op.base_op import BaseOp
|
3
|
-
from llmflow.schema.experience import vector_node_to_experience, dict_to_experience, BaseExperience
|
4
|
-
from llmflow.schema.request import VectorStoreRequest
|
5
|
-
from llmflow.schema.response import VectorStoreResponse
|
6
|
-
from llmflow.schema.vector_node import VectorNode
|
7
|
-
|
8
|
-
|
9
|
-
@OP_REGISTRY.register()
|
10
|
-
class VectorStoreActionOp(BaseOp):
|
11
|
-
|
12
|
-
def execute(self):
|
13
|
-
request: VectorStoreRequest = self.context.request
|
14
|
-
response: VectorStoreResponse = self.context.response
|
15
|
-
|
16
|
-
if request.action == "copy":
|
17
|
-
result = self.vector_store.copy_workspace(src_workspace_id=request.src_workspace_id,
|
18
|
-
dest_workspace_id=request.workspace_id)
|
19
|
-
|
20
|
-
elif request.action == "delete":
|
21
|
-
result = self.vector_store.delete_workspace(workspace_id=request.workspace_id)
|
22
|
-
|
23
|
-
elif request.action == "dump":
|
24
|
-
def node_to_experience(node: VectorNode) -> dict:
|
25
|
-
return vector_node_to_experience(node).model_dump()
|
26
|
-
|
27
|
-
result = self.vector_store.dump_workspace(workspace_id=request.workspace_id,
|
28
|
-
path=request.path,
|
29
|
-
callback_fn=node_to_experience)
|
30
|
-
|
31
|
-
elif request.action == "load":
|
32
|
-
def experience_dict_to_node(experience_dict: dict) -> VectorNode:
|
33
|
-
experience: BaseExperience = dict_to_experience(experience_dict=experience_dict)
|
34
|
-
return experience.to_vector_node()
|
35
|
-
|
36
|
-
result = self.vector_store.load_workspace(workspace_id=request.workspace_id,
|
37
|
-
path=request.path,
|
38
|
-
callback_fn=experience_dict_to_node)
|
39
|
-
|
40
|
-
else:
|
41
|
-
raise ValueError(f"invalid action={request.action}")
|
42
|
-
|
43
|
-
if isinstance(result, dict):
|
44
|
-
response.metadata.update(result)
|
45
|
-
else:
|
46
|
-
response.metadata["result"] = str(result)
|
llmflow/pipeline/pipeline.py
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
from concurrent.futures import as_completed
|
2
|
-
from itertools import zip_longest
|
3
|
-
from typing import List
|
4
|
-
|
5
|
-
from loguru import logger
|
6
|
-
|
7
|
-
from llmflow.op import OP_REGISTRY
|
8
|
-
from llmflow.op.base_op import BaseOp
|
9
|
-
from llmflow.pipeline.pipeline_context import PipelineContext
|
10
|
-
from llmflow.utils.timer import Timer, timer
|
11
|
-
|
12
|
-
|
13
|
-
class Pipeline:
|
14
|
-
seq_symbol: str = "->"
|
15
|
-
parallel_symbol: str = "|"
|
16
|
-
|
17
|
-
def __init__(self, pipeline: str, context: PipelineContext):
|
18
|
-
self.pipeline_list: List[str | List[str]] = self._parse_pipline(pipeline)
|
19
|
-
self.context: PipelineContext = context
|
20
|
-
|
21
|
-
def _parse_pipline(self, pipeline: str) -> List[str | List[str]]:
|
22
|
-
pipeline_list: List[str | List[str]] = []
|
23
|
-
|
24
|
-
for pipeline_split1 in pipeline.split("["):
|
25
|
-
for sub_pipeline in pipeline_split1.split("]"):
|
26
|
-
sub_pipeline = sub_pipeline.strip().strip(self.seq_symbol)
|
27
|
-
if not sub_pipeline:
|
28
|
-
continue
|
29
|
-
|
30
|
-
if self.parallel_symbol in sub_pipeline:
|
31
|
-
pipeline_list.append(sub_pipeline.split(self.parallel_symbol))
|
32
|
-
else:
|
33
|
-
pipeline_list.append(sub_pipeline)
|
34
|
-
logger.info(f"add sub_pipeline={sub_pipeline}")
|
35
|
-
return pipeline_list
|
36
|
-
|
37
|
-
def _execute_sub_pipeline(self, pipeline: str):
|
38
|
-
op_config_dict = self.context.app_config.op
|
39
|
-
for op in pipeline.split(self.seq_symbol):
|
40
|
-
op = op.strip()
|
41
|
-
if not op:
|
42
|
-
continue
|
43
|
-
|
44
|
-
assert op in op_config_dict, f"op={op} config is missing!"
|
45
|
-
op_config = op_config_dict[op]
|
46
|
-
|
47
|
-
assert op_config.backend in OP_REGISTRY, f"op={op} backend={op_config.backend} is not registered!"
|
48
|
-
op_cls = OP_REGISTRY[op_config.backend]
|
49
|
-
|
50
|
-
op_obj: BaseOp = op_cls(context=self.context, op_config=op_config)
|
51
|
-
op_obj.execute_wrap()
|
52
|
-
|
53
|
-
def _parse_sub_pipeline(self, pipeline: str):
|
54
|
-
for op in pipeline.split(self.seq_symbol):
|
55
|
-
op = op.strip()
|
56
|
-
if not op:
|
57
|
-
continue
|
58
|
-
|
59
|
-
yield op
|
60
|
-
|
61
|
-
def print_pipeline(self):
|
62
|
-
i: int = 0
|
63
|
-
for pipeline in self.pipeline_list:
|
64
|
-
if isinstance(pipeline, str):
|
65
|
-
for op in self._parse_sub_pipeline(pipeline):
|
66
|
-
i += 1
|
67
|
-
logger.info(f"stage_{i}: {op}")
|
68
|
-
|
69
|
-
elif isinstance(pipeline, list):
|
70
|
-
parallel_pipeline = [self._parse_sub_pipeline(x) for x in pipeline]
|
71
|
-
for op_list in zip_longest(*parallel_pipeline, fillvalue="-"):
|
72
|
-
i += 1
|
73
|
-
logger.info(f"stage{i}: {' | '.join(op_list)}")
|
74
|
-
else:
|
75
|
-
raise ValueError(f"unknown pipeline.type={type(pipeline)}")
|
76
|
-
|
77
|
-
@timer(name="pipeline.execute")
|
78
|
-
def __call__(self, enable_print: bool = True):
|
79
|
-
if enable_print:
|
80
|
-
self.print_pipeline()
|
81
|
-
|
82
|
-
for i, pipeline in enumerate(self.pipeline_list):
|
83
|
-
with Timer(f"step_{i}"):
|
84
|
-
if isinstance(pipeline, str):
|
85
|
-
self._execute_sub_pipeline(pipeline)
|
86
|
-
|
87
|
-
else:
|
88
|
-
future_list = []
|
89
|
-
for sub_pipeline in pipeline:
|
90
|
-
future = self.context.thread_pool.submit(self._execute_sub_pipeline, pipeline=sub_pipeline)
|
91
|
-
future_list.append(future)
|
92
|
-
|
93
|
-
for future in as_completed(future_list):
|
94
|
-
future.result()
|
@@ -1,37 +0,0 @@
|
|
1
|
-
from concurrent.futures import ThreadPoolExecutor
|
2
|
-
from typing import Dict
|
3
|
-
|
4
|
-
from llmflow.schema.app_config import AppConfig
|
5
|
-
from llmflow.vector_store.base_vector_store import BaseVectorStore
|
6
|
-
|
7
|
-
|
8
|
-
class PipelineContext:
|
9
|
-
|
10
|
-
def __init__(self, **kwargs):
|
11
|
-
self._context: dict = {**kwargs}
|
12
|
-
|
13
|
-
def get_context(self, key: str, default=None):
|
14
|
-
return self._context.get(key, default)
|
15
|
-
|
16
|
-
def set_context(self, key: str, value):
|
17
|
-
self._context[key] = value
|
18
|
-
|
19
|
-
@property
|
20
|
-
def request(self):
|
21
|
-
return self._context["request"]
|
22
|
-
|
23
|
-
@property
|
24
|
-
def response(self):
|
25
|
-
return self._context["response"]
|
26
|
-
|
27
|
-
@property
|
28
|
-
def app_config(self) -> AppConfig:
|
29
|
-
return self._context["app_config"]
|
30
|
-
|
31
|
-
@property
|
32
|
-
def thread_pool(self) -> ThreadPoolExecutor:
|
33
|
-
return self._context["thread_pool"]
|
34
|
-
|
35
|
-
@property
|
36
|
-
def vector_store_dict(self) -> Dict[str, BaseVectorStore]:
|
37
|
-
return self._context["vector_store_dict"]
|
llmflow/schema/app_config.py
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass, field
|
2
|
-
from typing import Dict
|
3
|
-
|
4
|
-
|
5
|
-
@dataclass
|
6
|
-
class HttpServiceConfig:
|
7
|
-
host: str = field(default="0.0.0.0")
|
8
|
-
port: int = field(default=8001)
|
9
|
-
timeout_keep_alive: int = field(default=600)
|
10
|
-
limit_concurrency: int = field(default=64)
|
11
|
-
|
12
|
-
|
13
|
-
@dataclass
|
14
|
-
class ThreadPoolConfig:
|
15
|
-
max_workers: int = field(default=10)
|
16
|
-
|
17
|
-
|
18
|
-
@dataclass
|
19
|
-
class APIConfig:
|
20
|
-
retriever: str = field(default="")
|
21
|
-
summarizer: str = field(default="")
|
22
|
-
vector_store: str = field(default="")
|
23
|
-
agent: str = field(default="")
|
24
|
-
|
25
|
-
|
26
|
-
@dataclass
|
27
|
-
class OpConfig:
|
28
|
-
backend: str = field(default="")
|
29
|
-
prompt_file_path: str = field(default="")
|
30
|
-
prompt_dict: dict = field(default_factory=dict)
|
31
|
-
llm: str = field(default="")
|
32
|
-
embedding_model: str = field(default="")
|
33
|
-
vector_store: str = field(default="")
|
34
|
-
params: dict = field(default_factory=dict)
|
35
|
-
|
36
|
-
|
37
|
-
@dataclass
|
38
|
-
class LLMConfig:
|
39
|
-
backend: str = field(default="")
|
40
|
-
model_name: str = field(default="")
|
41
|
-
params: dict = field(default_factory=dict)
|
42
|
-
|
43
|
-
|
44
|
-
@dataclass
|
45
|
-
class EmbeddingModelConfig:
|
46
|
-
backend: str = field(default="")
|
47
|
-
model_name: str = field(default="")
|
48
|
-
params: dict = field(default_factory=dict)
|
49
|
-
|
50
|
-
|
51
|
-
@dataclass
|
52
|
-
class VectorStoreConfig:
|
53
|
-
backend: str = field(default="")
|
54
|
-
embedding_model: str = field(default="")
|
55
|
-
params: dict = field(default_factory=dict)
|
56
|
-
|
57
|
-
|
58
|
-
@dataclass
|
59
|
-
class AppConfig:
|
60
|
-
pre_defined_config: str = field(default="mock_config")
|
61
|
-
config_path: str = field(default="")
|
62
|
-
mcp_transport: str = field(default="sse")
|
63
|
-
http_service: HttpServiceConfig = field(default_factory=HttpServiceConfig)
|
64
|
-
thread_pool: ThreadPoolConfig = field(default_factory=ThreadPoolConfig)
|
65
|
-
api: APIConfig = field(default_factory=APIConfig)
|
66
|
-
op: Dict[str, OpConfig] = field(default_factory=dict)
|
67
|
-
llm: Dict[str, LLMConfig] = field(default_factory=dict)
|
68
|
-
embedding_model: Dict[str, EmbeddingModelConfig] = field(default_factory=dict)
|
69
|
-
vector_store: Dict[str, VectorStoreConfig] = field(default_factory=dict)
|
llmflow/schema/experience.py
DELETED
@@ -1,144 +0,0 @@
|
|
1
|
-
import datetime
|
2
|
-
from abc import ABC
|
3
|
-
from typing import List
|
4
|
-
from uuid import uuid4
|
5
|
-
|
6
|
-
from loguru import logger
|
7
|
-
from pydantic import BaseModel, Field
|
8
|
-
|
9
|
-
from llmflow.schema.vector_node import VectorNode
|
10
|
-
|
11
|
-
|
12
|
-
class ExperienceMeta(BaseModel):
|
13
|
-
author: str = Field(default="")
|
14
|
-
created_time: str = Field(default_factory=lambda: datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
15
|
-
modified_time: str = Field(default_factory=lambda: datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
16
|
-
extra_info: dict | None = Field(default=None)
|
17
|
-
|
18
|
-
def update_modified_time(self):
|
19
|
-
self.modified_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
20
|
-
|
21
|
-
|
22
|
-
class BaseExperience(BaseModel, ABC):
|
23
|
-
workspace_id: str = Field(default="")
|
24
|
-
|
25
|
-
experience_id: str = Field(default_factory=lambda: uuid4().hex)
|
26
|
-
experience_type: str = Field(default="")
|
27
|
-
|
28
|
-
when_to_use: str = Field(default="")
|
29
|
-
content: str | bytes = Field(default="")
|
30
|
-
score: float | None = Field(default=None)
|
31
|
-
metadata: ExperienceMeta = Field(default_factory=ExperienceMeta)
|
32
|
-
|
33
|
-
def to_vector_node(self) -> VectorNode:
|
34
|
-
raise NotImplementedError
|
35
|
-
|
36
|
-
@classmethod
|
37
|
-
def from_vector_node(cls, node: VectorNode):
|
38
|
-
raise NotImplementedError
|
39
|
-
|
40
|
-
|
41
|
-
class TextExperience(BaseExperience):
|
42
|
-
experience_type: str = Field(default="text")
|
43
|
-
|
44
|
-
def to_vector_node(self) -> VectorNode:
|
45
|
-
return VectorNode(unique_id=self.experience_id,
|
46
|
-
workspace_id=self.workspace_id,
|
47
|
-
content=self.when_to_use,
|
48
|
-
metadata={
|
49
|
-
"experience_type": self.experience_type,
|
50
|
-
"experience_content": self.content,
|
51
|
-
"score": self.score,
|
52
|
-
"metadata": self.metadata.model_dump(),
|
53
|
-
})
|
54
|
-
|
55
|
-
@classmethod
|
56
|
-
def from_vector_node(cls, node: VectorNode):
|
57
|
-
return cls(workspace_id=node.workspace_id,
|
58
|
-
experience_id=node.unique_id,
|
59
|
-
experience_type=node.metadata.get("experience_type"),
|
60
|
-
when_to_use=node.content,
|
61
|
-
content=node.metadata.get("experience_content"),
|
62
|
-
score=node.metadata.get("score"),
|
63
|
-
metadata=node.metadata.get("metadata"))
|
64
|
-
|
65
|
-
|
66
|
-
class FunctionArg(BaseModel):
|
67
|
-
arg_name: str = Field(default=...)
|
68
|
-
arg_type: str = Field(default=...)
|
69
|
-
required: bool = Field(default=True)
|
70
|
-
|
71
|
-
|
72
|
-
class Function(BaseModel):
|
73
|
-
func_code: str = Field(default=..., description="function code")
|
74
|
-
func_name: str = Field(default=..., description="function name")
|
75
|
-
func_args: List[FunctionArg] = Field(default_factory=list)
|
76
|
-
|
77
|
-
|
78
|
-
class FuncExperience(BaseExperience):
|
79
|
-
experience_type: str = Field(default="function")
|
80
|
-
functions: List[Function] = Field(default_factory=list)
|
81
|
-
|
82
|
-
|
83
|
-
class PersonalExperience(BaseExperience):
|
84
|
-
experience_type: str = Field(default="personal")
|
85
|
-
person: str = Field(default="")
|
86
|
-
topic: str = Field(default="")
|
87
|
-
|
88
|
-
|
89
|
-
class KnowledgeExperience(BaseExperience):
|
90
|
-
experience_type: str = Field(default="knowledge")
|
91
|
-
topic: str = Field(default="")
|
92
|
-
|
93
|
-
|
94
|
-
def vector_node_to_experience(node: VectorNode) -> BaseExperience:
|
95
|
-
experience_type = node.metadata.get("experience_type")
|
96
|
-
if experience_type == "text":
|
97
|
-
return TextExperience.from_vector_node(node)
|
98
|
-
|
99
|
-
elif experience_type == "function":
|
100
|
-
return FuncExperience.from_vector_node(node)
|
101
|
-
|
102
|
-
elif experience_type == "personal":
|
103
|
-
return PersonalExperience.from_vector_node(node)
|
104
|
-
|
105
|
-
elif experience_type == "knowledge":
|
106
|
-
return KnowledgeExperience.from_vector_node(node)
|
107
|
-
|
108
|
-
else:
|
109
|
-
logger.warning(f"experience type {experience_type} not supported")
|
110
|
-
return TextExperience.from_vector_node(node)
|
111
|
-
|
112
|
-
|
113
|
-
def dict_to_experience(experience_dict: dict) -> BaseExperience:
|
114
|
-
experience_type = experience_dict.get("experience_type", "text")
|
115
|
-
if experience_type == "text":
|
116
|
-
return TextExperience(**experience_dict)
|
117
|
-
|
118
|
-
elif experience_type == "function":
|
119
|
-
return FuncExperience(**experience_dict)
|
120
|
-
|
121
|
-
elif experience_type == "personal":
|
122
|
-
return PersonalExperience(**experience_dict)
|
123
|
-
|
124
|
-
elif experience_type == "knowledge":
|
125
|
-
return KnowledgeExperience(**experience_dict)
|
126
|
-
|
127
|
-
else:
|
128
|
-
logger.warning(f"experience type {experience_type} not supported")
|
129
|
-
return TextExperience(**experience_dict)
|
130
|
-
|
131
|
-
|
132
|
-
if __name__ == "__main__":
|
133
|
-
e1 = TextExperience(
|
134
|
-
workspace_id="w_1024",
|
135
|
-
experience_id="123",
|
136
|
-
when_to_use="test case use",
|
137
|
-
content="test content",
|
138
|
-
score=0.99,
|
139
|
-
metadata=ExperienceMeta(author="user"))
|
140
|
-
print(e1.model_dump_json(indent=2))
|
141
|
-
v1 = e1.to_vector_node()
|
142
|
-
print(v1.model_dump_json(indent=2))
|
143
|
-
e2 = vector_node_to_experience(v1)
|
144
|
-
print(e2.model_dump_json(indent=2))
|