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.
Files changed (105) hide show
  1. flowllm/__init__.py +12 -0
  2. flowllm/app.py +25 -0
  3. flowllm/config/default_config.yaml +82 -0
  4. flowllm/config/pydantic_config_parser.py +242 -0
  5. flowllm/context/base_context.py +59 -0
  6. flowllm/context/flow_context.py +28 -0
  7. llmflow/op/prompt_mixin.py → flowllm/context/prompt_handler.py +25 -14
  8. flowllm/context/registry.py +26 -0
  9. flowllm/context/service_context.py +103 -0
  10. flowllm/embedding_model/__init__.py +1 -0
  11. {llmflow → flowllm}/embedding_model/base_embedding_model.py +2 -2
  12. {llmflow → flowllm}/embedding_model/openai_compatible_embedding_model.py +8 -8
  13. flowllm/flow_engine/__init__.py +1 -0
  14. flowllm/flow_engine/base_flow_engine.py +34 -0
  15. flowllm/flow_engine/simple_flow_engine.py +213 -0
  16. flowllm/llm/__init__.py +1 -0
  17. {llmflow → flowllm}/llm/base_llm.py +16 -24
  18. {llmflow → flowllm}/llm/openai_compatible_llm.py +64 -108
  19. flowllm/op/__init__.py +3 -0
  20. flowllm/op/akshare/get_ak_a_code_op.py +116 -0
  21. flowllm/op/akshare/get_ak_a_code_prompt.yaml +21 -0
  22. flowllm/op/akshare/get_ak_a_info_op.py +143 -0
  23. flowllm/op/base_op.py +169 -0
  24. flowllm/op/llm_base_op.py +63 -0
  25. flowllm/op/mock_op.py +42 -0
  26. flowllm/op/parallel_op.py +30 -0
  27. flowllm/op/sequential_op.py +29 -0
  28. flowllm/schema/flow_response.py +12 -0
  29. flowllm/schema/message.py +35 -0
  30. flowllm/schema/service_config.py +76 -0
  31. flowllm/schema/tool_call.py +110 -0
  32. flowllm/service/__init__.py +2 -0
  33. flowllm/service/base_service.py +59 -0
  34. flowllm/service/http_service.py +87 -0
  35. flowllm/service/mcp_service.py +45 -0
  36. flowllm/storage/__init__.py +1 -0
  37. flowllm/storage/vector_store/__init__.py +3 -0
  38. flowllm/storage/vector_store/base_vector_store.py +44 -0
  39. {llmflow → flowllm/storage}/vector_store/chroma_vector_store.py +11 -10
  40. {llmflow → flowllm/storage}/vector_store/es_vector_store.py +10 -9
  41. llmflow/vector_store/file_vector_store.py → flowllm/storage/vector_store/local_vector_store.py +110 -10
  42. flowllm/utils/common_utils.py +64 -0
  43. flowllm/utils/dataframe_cache.py +331 -0
  44. flowllm/utils/fetch_url.py +113 -0
  45. {llmflow → flowllm}/utils/timer.py +5 -4
  46. {flowllm-0.1.0.dist-info → flowllm-0.1.1.dist-info}/METADATA +31 -27
  47. flowllm-0.1.1.dist-info/RECORD +62 -0
  48. flowllm-0.1.1.dist-info/entry_points.txt +4 -0
  49. {flowllm-0.1.0.dist-info → flowllm-0.1.1.dist-info}/licenses/LICENSE +1 -1
  50. flowllm-0.1.1.dist-info/top_level.txt +1 -0
  51. flowllm-0.1.0.dist-info/RECORD +0 -66
  52. flowllm-0.1.0.dist-info/entry_points.txt +0 -3
  53. flowllm-0.1.0.dist-info/top_level.txt +0 -1
  54. llmflow/app.py +0 -53
  55. llmflow/config/config_parser.py +0 -80
  56. llmflow/config/mock_config.yaml +0 -58
  57. llmflow/embedding_model/__init__.py +0 -5
  58. llmflow/enumeration/agent_state.py +0 -8
  59. llmflow/llm/__init__.py +0 -5
  60. llmflow/mcp_server.py +0 -110
  61. llmflow/op/__init__.py +0 -10
  62. llmflow/op/base_op.py +0 -125
  63. llmflow/op/mock_op.py +0 -40
  64. llmflow/op/react/react_v1_op.py +0 -88
  65. llmflow/op/react/react_v1_prompt.yaml +0 -28
  66. llmflow/op/vector_store/__init__.py +0 -13
  67. llmflow/op/vector_store/recall_vector_store_op.py +0 -48
  68. llmflow/op/vector_store/update_vector_store_op.py +0 -28
  69. llmflow/op/vector_store/vector_store_action_op.py +0 -46
  70. llmflow/pipeline/pipeline.py +0 -94
  71. llmflow/pipeline/pipeline_context.py +0 -37
  72. llmflow/schema/app_config.py +0 -69
  73. llmflow/schema/experience.py +0 -144
  74. llmflow/schema/message.py +0 -68
  75. llmflow/schema/request.py +0 -32
  76. llmflow/schema/response.py +0 -29
  77. llmflow/service/__init__.py +0 -0
  78. llmflow/service/llmflow_service.py +0 -96
  79. llmflow/tool/__init__.py +0 -9
  80. llmflow/tool/base_tool.py +0 -80
  81. llmflow/tool/code_tool.py +0 -43
  82. llmflow/tool/dashscope_search_tool.py +0 -162
  83. llmflow/tool/mcp_tool.py +0 -77
  84. llmflow/tool/tavily_search_tool.py +0 -109
  85. llmflow/tool/terminate_tool.py +0 -23
  86. llmflow/utils/__init__.py +0 -0
  87. llmflow/utils/common_utils.py +0 -17
  88. llmflow/utils/file_handler.py +0 -25
  89. llmflow/utils/http_client.py +0 -156
  90. llmflow/utils/op_utils.py +0 -102
  91. llmflow/utils/registry.py +0 -33
  92. llmflow/vector_store/__init__.py +0 -7
  93. llmflow/vector_store/base_vector_store.py +0 -136
  94. {llmflow → flowllm/config}/__init__.py +0 -0
  95. {llmflow/config → flowllm/context}/__init__.py +0 -0
  96. {llmflow → flowllm}/enumeration/__init__.py +0 -0
  97. {llmflow → flowllm}/enumeration/chunk_enum.py +0 -0
  98. {llmflow → flowllm}/enumeration/http_enum.py +0 -0
  99. {llmflow → flowllm}/enumeration/role.py +0 -0
  100. {llmflow/op/react → flowllm/op/akshare}/__init__.py +0 -0
  101. {llmflow/pipeline → flowllm/schema}/__init__.py +0 -0
  102. {llmflow → flowllm}/schema/vector_node.py +0 -0
  103. {llmflow/schema → flowllm/utils}/__init__.py +0 -0
  104. {llmflow → flowllm}/utils/singleton.py +0 -0
  105. {flowllm-0.1.0.dist-info → flowllm-0.1.1.dist-info}/WHEEL +0 -0
flowllm/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ from flowllm import embedding_model
2
+ from flowllm import flow_engine
3
+ from flowllm import llm
4
+ from flowllm import op
5
+ from flowllm import service
6
+ from flowllm import storage
7
+
8
+ from .app import main
9
+
10
+ __version__ = "0.1.1"
11
+
12
+ __all__ = ["main"]
flowllm/app.py ADDED
@@ -0,0 +1,25 @@
1
+ import sys
2
+
3
+ from flowllm.service.base_service import BaseService
4
+ from flowllm.utils.common_utils import load_env
5
+
6
+ load_env()
7
+
8
+ from flowllm.config.pydantic_config_parser import PydanticConfigParser
9
+ from flowllm.schema.service_config import ServiceConfig
10
+ from flowllm.context.service_context import C
11
+
12
+
13
+ def main():
14
+ config_parser = PydanticConfigParser(ServiceConfig)
15
+ service_config: ServiceConfig = config_parser.parse_args(*sys.argv[1:])
16
+ service_cls = C.resolve_service(service_config.backend)
17
+ service: BaseService = service_cls(service_config)
18
+ service()
19
+
20
+ if __name__ == "__main__":
21
+ main()
22
+
23
+
24
+ # python -m build
25
+ # twine upload dist/*
@@ -0,0 +1,82 @@
1
+ # default config.yaml
2
+ backend: mcp
3
+ language: ""
4
+ thread_pool_max_workers: 32
5
+ ray_max_workers: 8
6
+
7
+ mcp:
8
+ transport: sse
9
+ host: "0.0.0.0"
10
+ port: 8001
11
+
12
+ http:
13
+ host: "0.0.0.0"
14
+ port: 8001
15
+ timeout_keep_alive: 600
16
+ limit_concurrency: 64
17
+
18
+
19
+ flow_engine:
20
+ backend: simple
21
+
22
+
23
+ flow:
24
+ get_a_stock_infos:
25
+ flow_content: get_ak_a_code_op >> get_ak_a_info_op >> get_ak_a_spot_op >> get_ak_a_money_flow_op >> get_ak_a_financial_info_op >> merge_ak_a_info_op
26
+ description: "Retrieve the A-share stock codes from the query, and fetch information about these stocks, including company basic information, current stock price and its change percentage, capital inflow and outflow data for the most recent day, and financial information from the latest quarter."
27
+ input_schema:
28
+ query:
29
+ type: "str"
30
+ description: "user question"
31
+
32
+ get_a_stock_news:
33
+ flow_content: get_ak_a_code_op >> get_ak_a_news_op >> merge_ak_a_info_op
34
+ description: "Retrieve the A-share stock codes from the query, and obtain the latest news information about these stocks."
35
+ input_schema:
36
+ query:
37
+ type: "str"
38
+ description: "user question"
39
+
40
+ mock_flow:
41
+ flow_content: mock1_op>>((mock4_op>>mock2_op)|mock5_op)>>(mock3_op|mock6_op)
42
+ description: "mock flow"
43
+ input_schema:
44
+ a:
45
+ type: "str"
46
+ description: "mock attr a"
47
+ required: true
48
+ b:
49
+ type: "str"
50
+ description: "mock attr b"
51
+ required: true
52
+
53
+ op:
54
+ mock1_op:
55
+ backend: mock1_op
56
+ llm: default
57
+ vector_store: default
58
+
59
+ llm:
60
+ default:
61
+ backend: openai_compatible
62
+ model_name: qwen3-30b-a3b-thinking-2507
63
+ params:
64
+ temperature: 0.6
65
+ qwen3_30b_instruct:
66
+ backend: openai_compatible
67
+ model_name: qwen3-30b-a3b-instruct-2507
68
+
69
+ embedding_model:
70
+ default:
71
+ backend: openai_compatible
72
+ model_name: text-embedding-v4
73
+ params:
74
+ dimensions: 1024
75
+
76
+ vector_store:
77
+ default:
78
+ backend: elasticsearch
79
+ embedding_model: default
80
+ params:
81
+ hosts: "http://localhost:9200"
82
+
@@ -0,0 +1,242 @@
1
+ import copy
2
+ import json
3
+ from pathlib import Path
4
+ from typing import Any, Generic, List, Type, TypeVar
5
+
6
+ import yaml
7
+ from loguru import logger
8
+ from pydantic import BaseModel
9
+
10
+ T = TypeVar('T', bound=BaseModel)
11
+
12
+
13
+ class PydanticConfigParser(Generic[T]):
14
+ """
15
+ Pydantic Configuration Parser
16
+
17
+ Supported configuration sources (priority from low to high):
18
+ 1. Default configuration (Pydantic model default values)
19
+ 2. YAML configuration file
20
+ 3. Command line arguments (dot notation format)
21
+ 4. Runtime parameters
22
+ """
23
+
24
+ def __init__(self, config_class: Type[T]):
25
+ """
26
+ Initialize configuration parser
27
+
28
+ Args:
29
+ config_class: Pydantic configuration model class
30
+ """
31
+ self.config_class = config_class
32
+ self.config_dict: dict = {}
33
+
34
+ def parse_dot_notation(self, dot_list: List[str]) -> dict:
35
+ """
36
+ Parse dot notation format configuration list
37
+
38
+ Args:
39
+ dot_list: Configuration list in format ['a.b.c=value', 'x.y=123']
40
+
41
+ Returns:
42
+ Parsed nested dictionary
43
+ """
44
+ config_dict = {}
45
+
46
+ for item in dot_list:
47
+ if '=' not in item:
48
+ continue
49
+
50
+ key_path, value_str = item.split('=', 1)
51
+ keys = key_path.split('.')
52
+
53
+ # Automatic type conversion
54
+ value = self._convert_value(value_str)
55
+
56
+ # Build nested dictionary
57
+ current_dict = config_dict
58
+ for key in keys[:-1]:
59
+ if key not in current_dict:
60
+ current_dict[key] = {}
61
+ current_dict = current_dict[key]
62
+
63
+ current_dict[keys[-1]] = value
64
+
65
+ return config_dict
66
+
67
+ @staticmethod
68
+ def _convert_value(value_str: str) -> Any:
69
+ """
70
+ Automatically convert string values to appropriate Python types
71
+
72
+ Args:
73
+ value_str: String value
74
+
75
+ Returns:
76
+ Converted value
77
+ """
78
+ value_str = value_str.strip()
79
+
80
+ if value_str.lower() in ("true", "false"):
81
+ return value_str.lower() == "true"
82
+
83
+ if value_str.lower() in ("none", "null"):
84
+ return None
85
+
86
+ try:
87
+ if "." not in value_str and "e" not in value_str.lower():
88
+ return int(value_str)
89
+
90
+ return float(value_str)
91
+
92
+ except ValueError:
93
+ pass
94
+
95
+ try:
96
+ return json.loads(value_str)
97
+ except (json.JSONDecodeError, ValueError):
98
+ pass
99
+
100
+ return value_str
101
+
102
+ @staticmethod
103
+ def load_from_yaml(yaml_path: str | Path) -> dict:
104
+ """
105
+ Load configuration from YAML file
106
+
107
+ Args:
108
+ yaml_path: YAML file path
109
+
110
+ Returns:
111
+ Configuration dictionary
112
+ """
113
+ if isinstance(yaml_path, str):
114
+ yaml_path = Path(yaml_path)
115
+
116
+ if not yaml_path.exists():
117
+ raise FileNotFoundError(f"Configuration file does not exist: {yaml_path}")
118
+
119
+ with yaml_path.open() as f:
120
+ return yaml.safe_load(f)
121
+
122
+ def merge_configs(self, *config_dicts: dict) -> dict:
123
+ """
124
+ Deep merge multiple configuration dictionaries
125
+
126
+ Args:
127
+ *config_dicts: Multiple configuration dictionaries
128
+
129
+ Returns:
130
+ Merged configuration dictionary
131
+ """
132
+ result = {}
133
+
134
+ for config_dict in config_dicts:
135
+ result = self._deep_merge(result, config_dict)
136
+
137
+ return result
138
+
139
+ def _deep_merge(self, base_dict: dict, update_dict: dict) -> dict:
140
+ """
141
+ Deep merge two dictionaries
142
+
143
+ Args:
144
+ base_dict: Base dictionary
145
+ update_dict: Update dictionary
146
+
147
+ Returns:
148
+ Merged dictionary
149
+ """
150
+ result = base_dict.copy()
151
+
152
+ for key, value in update_dict.items():
153
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
154
+ result[key] = self._deep_merge(result[key], value)
155
+ else:
156
+ result[key] = value
157
+
158
+ return result
159
+
160
+ def parse_args(self, *args) -> T:
161
+ """
162
+ Parse command line arguments and return configuration object
163
+
164
+ Args:
165
+ args: Command line arguments.
166
+
167
+ Returns:
168
+ Parsed configuration object
169
+ """
170
+ configs_to_merge = []
171
+
172
+ # 1. Default configuration (from Pydantic model)
173
+ default_config = self.config_class().model_dump()
174
+ configs_to_merge.append(default_config)
175
+
176
+ # 2. YAML configuration file
177
+ config = ""
178
+ filter_args = []
179
+ for arg in args:
180
+ if "=" not in arg:
181
+ continue
182
+
183
+ arg = arg.lstrip("--").lstrip("-")
184
+
185
+ if "c=" in arg or "config=" in arg:
186
+ config = arg.split("=")[-1]
187
+ else:
188
+ filter_args.append(arg)
189
+
190
+ if config:
191
+ if not config.endswith(".yaml"):
192
+ config += ".yaml"
193
+
194
+ # load pre-built configs
195
+ config_path = Path(__file__).parent / config
196
+ if not config_path.exists():
197
+ config_path = Path(config)
198
+
199
+ yaml_config = self.load_from_yaml(config_path)
200
+ logger.info(f"flowllm using config={config_path}")
201
+ configs_to_merge.append(yaml_config)
202
+
203
+ # 3. Command line override configuration
204
+ if args:
205
+ cli_config = self.parse_dot_notation(filter_args)
206
+ configs_to_merge.append(cli_config)
207
+
208
+ # Merge all configurations
209
+ self.config_dict = self.merge_configs(*configs_to_merge)
210
+
211
+ # Create and validate final configuration object
212
+ return self.config_class.model_validate(self.config_dict)
213
+
214
+ def update_config(self, **kwargs) -> T:
215
+ """
216
+ Update configuration object using keyword arguments
217
+
218
+ Args:
219
+ **kwargs: Configuration items to update, supports dot notation, e.g. server__host='localhost'
220
+
221
+ Returns:
222
+ Updated configuration object
223
+ """
224
+ # Convert kwargs to dot notation format
225
+ dot_list = []
226
+ for key, value in kwargs.items():
227
+ # support double underscore as dot replacement (server__host -> server.host)
228
+ dot_key = key.replace("__", ".")
229
+ dot_list.append(f"{dot_key}={value}")
230
+
231
+ # Parse and merge configuration
232
+ override_config = self.parse_dot_notation(dot_list)
233
+ final_config = self.merge_configs(copy.deepcopy(self.config_dict), override_config)
234
+
235
+ return self.config_class.model_validate(final_config)
236
+
237
+
238
+ def get_default_config():
239
+ from flowllm.schema.service_config import ServiceConfig
240
+
241
+ config_parser = PydanticConfigParser(ServiceConfig)
242
+ return config_parser.parse_args("config=default_config")
@@ -0,0 +1,59 @@
1
+ from typing import List
2
+
3
+
4
+ class BaseContext:
5
+ def __init__(self, **kwargs):
6
+ self._data = {**kwargs}
7
+
8
+ def __getattr__(self, name: str):
9
+ if name in self._data:
10
+ return self._data[name]
11
+
12
+ raise AttributeError(f"'{self.__class__.__name__}' has no attribute '{name}'")
13
+
14
+ def __setattr__(self, name: str, value):
15
+ if name == "_data":
16
+ super().__setattr__(name, value)
17
+ else:
18
+ self._data[name] = value
19
+
20
+ def __getitem__(self, name: str):
21
+ if name not in self._data:
22
+ raise AttributeError(f"'{self.__class__.__name__}' has no attribute '{name}'")
23
+ return self._data[name]
24
+
25
+ def __setitem__(self, name: str, value):
26
+ self._data[name] = value
27
+
28
+ def __contains__(self, name: str):
29
+ return name in self._data
30
+
31
+ def __repr__(self):
32
+ return f"{self.__class__.__name__}({self._data!r})"
33
+
34
+ def dump(self) -> dict:
35
+ return self._data
36
+
37
+ @property
38
+ def keys(self) -> List[str]:
39
+ return sorted(self._data.keys())
40
+
41
+ def update(self, **kwargs):
42
+ self._data.update(kwargs)
43
+
44
+
45
+ if __name__ == "__main__":
46
+ ctx = BaseContext(**{"name": "Alice", "age": 30, "city": "New York"})
47
+
48
+ print(ctx.name)
49
+ print(ctx.age)
50
+ print(ctx.city)
51
+
52
+ ctx.email = "alice@example.com"
53
+ ctx["email"] = "alice@example.com"
54
+ print(ctx.email)
55
+
56
+ print(ctx.keys)
57
+ print(ctx)
58
+
59
+ # print(ctx.city2)
@@ -0,0 +1,28 @@
1
+ import uuid
2
+
3
+ from flowllm.context.base_context import BaseContext
4
+ from flowllm.schema.flow_response import FlowResponse
5
+ from flowllm.schema.service_config import ServiceConfig
6
+
7
+
8
+ class FlowContext(BaseContext):
9
+
10
+ def __init__(self, flow_id: str = uuid.uuid4().hex, **kwargs):
11
+ super().__init__(**kwargs)
12
+ self.flow_id: str = flow_id
13
+
14
+ @property
15
+ def service_config(self) -> ServiceConfig:
16
+ return self._data.get("service_config")
17
+
18
+ @service_config.setter
19
+ def service_config(self, service_config: ServiceConfig):
20
+ self._data["service_config"] = service_config
21
+
22
+ @property
23
+ def response(self) -> FlowResponse:
24
+ return self._data.get("response")
25
+
26
+ @response.setter
27
+ def response(self, response: FlowResponse):
28
+ self._data["response"] = response
@@ -3,42 +3,56 @@ from pathlib import Path
3
3
  import yaml
4
4
  from loguru import logger
5
5
 
6
+ from flowllm.context.base_context import BaseContext
7
+ from flowllm.context.service_context import C
6
8
 
7
- class PromptMixin:
8
9
 
9
- def __init__(self):
10
- self._prompt_dict: dict = {}
10
+ class PromptHandler(BaseContext):
11
+
12
+ def __init__(self, language: str = "", **kwargs):
13
+ super().__init__(**kwargs)
14
+ self.language: str = language or C.language
11
15
 
12
16
  def load_prompt_by_file(self, prompt_file_path: Path | str = None):
13
17
  if prompt_file_path is None:
14
- return
18
+ return self
15
19
 
16
20
  if isinstance(prompt_file_path, str):
17
21
  prompt_file_path = Path(prompt_file_path)
18
22
 
19
23
  if not prompt_file_path.exists():
20
- return
24
+ return self
21
25
 
22
26
  with prompt_file_path.open() as f:
23
27
  prompt_dict = yaml.load(f, yaml.FullLoader)
24
28
  self.load_prompt_dict(prompt_dict)
29
+ return self
25
30
 
26
31
  def load_prompt_dict(self, prompt_dict: dict = None):
27
32
  if not prompt_dict:
28
- return
33
+ return self
29
34
 
30
35
  for key, value in prompt_dict.items():
31
36
  if isinstance(value, str):
32
- if key in self._prompt_dict:
33
- self._prompt_dict[key] = value
37
+ if key in self._data:
38
+ self._data[key] = value
34
39
  logger.warning(f"prompt_dict key={key} overwrite!")
35
40
 
36
41
  else:
37
- self._prompt_dict[key] = value
42
+ self._data[key] = value
38
43
  logger.info(f"add prompt_dict key={key}")
44
+ return self
45
+
46
+ def get_prompt(self, prompt_name: str):
47
+ key: str = prompt_name
48
+ if self.language and not key.endswith(self.language.strip()):
49
+ key += "_" + self.language.strip()
39
50
 
40
- def prompt_format(self, prompt_name: str, **kwargs):
41
- prompt = self._prompt_dict[prompt_name]
51
+ assert key in self._data, f"prompt_name={key} not found."
52
+ return self._data[key]
53
+
54
+ def prompt_format(self, prompt_name: str, **kwargs) -> str:
55
+ prompt = self.get_prompt(prompt_name)
42
56
 
43
57
  flag_kwargs = {k: v for k, v in kwargs.items() if isinstance(v, bool)}
44
58
  other_kwargs = {k: v for k, v in kwargs.items() if not isinstance(v, bool)}
@@ -69,6 +83,3 @@ class PromptMixin:
69
83
  prompt = prompt.format(**other_kwargs)
70
84
 
71
85
  return prompt
72
-
73
- def get_prompt(self, key: str):
74
- return self._prompt_dict[key]
@@ -0,0 +1,26 @@
1
+ from loguru import logger
2
+
3
+ from flowllm.context.base_context import BaseContext
4
+ from flowllm.utils.common_utils import camel_to_snake
5
+
6
+
7
+ class Registry(BaseContext):
8
+
9
+ def __init__(self, registry_name: str, enable_log: bool = True, **kwargs):
10
+ super().__init__(**kwargs)
11
+ self.registry_name: str = registry_name
12
+ self.enable_log: bool = enable_log
13
+
14
+ def register(self, name: str = ""):
15
+ def decorator(cls):
16
+ class_name = name if name else camel_to_snake(cls.__name__)
17
+ if self.enable_log:
18
+ if class_name in self._data:
19
+ logger.warning(f"{self.registry_name}.class({class_name}) is already registered!")
20
+ else:
21
+ logger.info(f"{self.registry_name}.class({class_name}) is registered.")
22
+
23
+ self._data[class_name] = cls
24
+ return cls
25
+
26
+ return decorator
@@ -0,0 +1,103 @@
1
+ import uuid
2
+ from concurrent.futures import ThreadPoolExecutor
3
+ from typing import Dict
4
+
5
+ from flowllm.context.base_context import BaseContext
6
+ from flowllm.context.registry import Registry
7
+ from flowllm.utils.singleton import singleton
8
+
9
+
10
+ @singleton
11
+ class ServiceContext(BaseContext):
12
+
13
+ def __init__(self, service_id: str = uuid.uuid4().hex, **kwargs):
14
+ super().__init__(**kwargs)
15
+ self.service_id: str = service_id
16
+ self.registry_dict: Dict[str, Registry] = \
17
+ {k: Registry(k) for k in ["embedding_model", "llm", "vector_store", "op", "flow_engine", "service"]}
18
+
19
+ @property
20
+ def language(self) -> str:
21
+ return self._data.get("language", "")
22
+
23
+ @language.setter
24
+ def language(self, value: str):
25
+ self._data["language"] = value
26
+
27
+ @property
28
+ def thread_pool(self) -> ThreadPoolExecutor:
29
+ return self._data["thread_pool"]
30
+
31
+ @thread_pool.setter
32
+ def thread_pool(self, thread_pool: ThreadPoolExecutor):
33
+ self._data["thread_pool"] = thread_pool
34
+
35
+ def get_vector_store(self, name: str = "default"):
36
+ vector_store_dict: dict = self._data["vector_store_dict"]
37
+ if name not in vector_store_dict:
38
+ raise KeyError(f"vector store {name} not found")
39
+
40
+ return vector_store_dict[name]
41
+
42
+ def set_vector_store(self, name: str, vector_store):
43
+ if "vector_store_dict" not in self._data:
44
+ self.set_vector_stores({})
45
+
46
+ self._data["vector_store_dict"][name] = vector_store
47
+
48
+ def set_vector_stores(self, vector_store_dict: dict):
49
+ self._data["vector_store_dict"] = vector_store_dict
50
+
51
+ """
52
+ register models
53
+ """
54
+
55
+ def register_embedding_model(self, name: str = ""):
56
+ return self.registry_dict["embedding_model"].register(name=name)
57
+
58
+ def register_llm(self, name: str = ""):
59
+ return self.registry_dict["llm"].register(name=name)
60
+
61
+ def register_vector_store(self, name: str = ""):
62
+ return self.registry_dict["vector_store"].register(name=name)
63
+
64
+ def register_op(self, name: str = ""):
65
+ return self.registry_dict["op"].register(name=name)
66
+
67
+ def register_flow_engine(self, name: str = ""):
68
+ return self.registry_dict["flow_engine"].register(name=name)
69
+
70
+ def register_service(self, name: str = ""):
71
+ return self.registry_dict["service"].register(name=name)
72
+
73
+ """
74
+ resolve models
75
+ """
76
+
77
+ def resolve_embedding_model(self, name: str):
78
+ assert name in self.registry_dict["embedding_model"], f"embedding_model={name} not found!"
79
+ return self.registry_dict["embedding_model"][name]
80
+
81
+ def resolve_llm(self, name: str):
82
+ assert name in self.registry_dict["llm"], f"llm={name} not found!"
83
+ return self.registry_dict["llm"][name]
84
+
85
+ def resolve_vector_store(self, name: str):
86
+ assert name in self.registry_dict["vector_store"], f"vector_store={name} not found!"
87
+ return self.registry_dict["vector_store"][name]
88
+
89
+ def resolve_op(self, name: str):
90
+ assert name in self.registry_dict["op"], f"op={name} not found!"
91
+ return self.registry_dict["op"][name]
92
+
93
+ def resolve_flow_engine(self, name: str):
94
+ assert name in self.registry_dict["flow_engine"], f"flow={name} not found!"
95
+ return self.registry_dict["flow_engine"][name]
96
+
97
+ def resolve_service(self, name: str):
98
+ assert name in self.registry_dict["service"], f"service={name} not found!"
99
+ return self.registry_dict["service"][name]
100
+
101
+
102
+
103
+ C = ServiceContext()
@@ -0,0 +1 @@
1
+ from flowllm.embedding_model.openai_compatible_embedding_model import OpenAICompatibleEmbeddingModel
@@ -4,7 +4,7 @@ from typing import List
4
4
  from loguru import logger
5
5
  from pydantic import BaseModel, Field
6
6
 
7
- from llmflow.schema.vector_node import VectorNode
7
+ from flowllm.schema.vector_node import VectorNode
8
8
 
9
9
 
10
10
  class BaseEmbeddingModel(BaseModel, ABC):
@@ -101,4 +101,4 @@ class BaseEmbeddingModel(BaseModel, ABC):
101
101
  return nodes
102
102
 
103
103
  else:
104
- raise RuntimeError(f"unsupported type={type(nodes)}")
104
+ raise TypeError(f"unsupported type={type(nodes)}")