alayaflow 0.1.1__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.
- alayaflow/__init__.py +1 -1
- alayaflow/api/api_singleton.py +13 -5
- alayaflow/component/search_node.py +147 -0
- alayaflow/execution/env_manager.py +1 -0
- alayaflow/workflow/__init__.py +5 -1
- alayaflow/workflow/workflow_loader.py +11 -7
- alayaflow/workflow/workflow_manager.py +18 -6
- {alayaflow-0.1.1.dist-info → alayaflow-0.1.2.dist-info}/METADATA +1 -1
- {alayaflow-0.1.1.dist-info → alayaflow-0.1.2.dist-info}/RECORD +11 -10
- {alayaflow-0.1.1.dist-info → alayaflow-0.1.2.dist-info}/WHEEL +0 -0
- {alayaflow-0.1.1.dist-info → alayaflow-0.1.2.dist-info}/licenses/LICENSE +0 -0
alayaflow/__init__.py
CHANGED
alayaflow/api/api_singleton.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
from typing import Generator, Dict, List, Self
|
|
1
|
+
from typing import Generator, Dict, List, Self, Optional
|
|
2
2
|
|
|
3
3
|
from alayaflow.utils.singleton import SingletonMeta
|
|
4
4
|
from alayaflow.workflow import WorkflowManager
|
|
5
5
|
from alayaflow.execution import ExecutorManager, ExecutorType
|
|
6
6
|
from alayaflow.common.config import settings
|
|
7
7
|
from alayaflow.component.model import ModelManager, ModelProfile
|
|
8
|
+
from alayaflow.workflow.runnable import BaseRunnableWorkflow
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class APISingleton(metaclass=SingletonMeta):
|
|
@@ -23,8 +24,10 @@ class APISingleton(metaclass=SingletonMeta):
|
|
|
23
24
|
if not self._inited:
|
|
24
25
|
raise ValueError("Flow APISingleton 未初始化,请先调用 init 方法")
|
|
25
26
|
|
|
26
|
-
def init(self, config: dict =
|
|
27
|
+
def init(self, config: dict = None) -> Self:
|
|
27
28
|
"""初始化 Flow APISingleton"""
|
|
29
|
+
if config is None:
|
|
30
|
+
config = {}
|
|
28
31
|
# Overwrite all for now
|
|
29
32
|
settings.alayahub_url = config.get("alayahub_url", settings.alayahub_url)
|
|
30
33
|
settings.langfuse_enabled = config.get("langfuse_enabled", settings.langfuse_enabled)
|
|
@@ -66,10 +69,15 @@ class APISingleton(metaclass=SingletonMeta):
|
|
|
66
69
|
self._check_init()
|
|
67
70
|
return self.workflow_manager.uninstall_workflow(workflow_id)
|
|
68
71
|
|
|
69
|
-
def load_workflow(self, workflow_id: str, version: str, init_args:
|
|
70
|
-
"""
|
|
72
|
+
def load_workflow(self, workflow_id: str, version: str, init_args: Optional[Dict] = None, storage_path: str = settings.workflow_storage_path) -> None:
|
|
73
|
+
"""加载本地工作流"""
|
|
71
74
|
self._check_init()
|
|
72
|
-
return self.workflow_manager.load_workflow(workflow_id, version, init_args)
|
|
75
|
+
return self.workflow_manager.load_workflow(workflow_id, version, init_args or {}, storage_path)
|
|
76
|
+
|
|
77
|
+
def register_workflow(self, runnable: BaseRunnableWorkflow, requirements: List[str] = None) -> None:
|
|
78
|
+
"""加载Python项目中的工作流"""
|
|
79
|
+
self._check_init()
|
|
80
|
+
self.workflow_manager.register_workflow(runnable, requirements or [])
|
|
73
81
|
|
|
74
82
|
def exec_workflow(
|
|
75
83
|
self,
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import urllib.parse
|
|
5
|
+
from typing import List, Dict, Any, Optional, Literal
|
|
6
|
+
|
|
7
|
+
class WebSearchJinaComponent:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
*,
|
|
11
|
+
api_key: str,
|
|
12
|
+
|
|
13
|
+
query: str,
|
|
14
|
+
|
|
15
|
+
top_k: int = 5,
|
|
16
|
+
site_limit: Optional[str] = None,
|
|
17
|
+
timeout: int = 60,
|
|
18
|
+
|
|
19
|
+
no_cache: bool = False,
|
|
20
|
+
|
|
21
|
+
response_format: Literal['markdown', 'html', 'text'] = 'markdown',
|
|
22
|
+
|
|
23
|
+
retain_images: Literal['none', 'all', 'occluded'] = 'none',
|
|
24
|
+
|
|
25
|
+
with_generated_alt: bool = True,
|
|
26
|
+
|
|
27
|
+
with_links_summary: bool = False,
|
|
28
|
+
):
|
|
29
|
+
self.api_key = api_key
|
|
30
|
+
self.query = query
|
|
31
|
+
self.top_k = top_k
|
|
32
|
+
self.site_limit = site_limit
|
|
33
|
+
self.timeout = timeout
|
|
34
|
+
|
|
35
|
+
self.no_cache = no_cache
|
|
36
|
+
self.response_format = response_format
|
|
37
|
+
self.retain_images = retain_images
|
|
38
|
+
self.with_generated_alt = with_generated_alt
|
|
39
|
+
self.with_links_summary = with_links_summary
|
|
40
|
+
|
|
41
|
+
self.base_url = "https://s.jina.ai/"
|
|
42
|
+
|
|
43
|
+
def _construct_query(self) -> str:
|
|
44
|
+
final_query = self.query
|
|
45
|
+
if self.site_limit:
|
|
46
|
+
final_query = f"{final_query} site:{self.site_limit}"
|
|
47
|
+
return final_query
|
|
48
|
+
|
|
49
|
+
def _get_headers(self) -> Dict[str, str]:
|
|
50
|
+
headers = {
|
|
51
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
52
|
+
"Accept": "application/json",
|
|
53
|
+
|
|
54
|
+
"X-Retain-Images": self.retain_images,
|
|
55
|
+
"X-Return-Format": self.response_format,
|
|
56
|
+
"X-With-Generated-Alt": "true" if self.with_generated_alt else "false",
|
|
57
|
+
"X-With-Links-Summary": "true" if self.with_links_summary else "false",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if self.no_cache:
|
|
61
|
+
headers["X-No-Cache"] = "true"
|
|
62
|
+
|
|
63
|
+
return headers
|
|
64
|
+
|
|
65
|
+
def __call__(self) -> List[Dict[str, Any]]:
|
|
66
|
+
query_str = self._construct_query()
|
|
67
|
+
encoded_query = urllib.parse.quote(query_str)
|
|
68
|
+
url = f"{self.base_url}{encoded_query}"
|
|
69
|
+
|
|
70
|
+
headers = self._get_headers()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
response = requests.get(url, headers=headers, timeout=self.timeout)
|
|
74
|
+
|
|
75
|
+
if response.status_code != 200:
|
|
76
|
+
print(f"Error: API returned status {response.status_code}")
|
|
77
|
+
print(f"Message: {response.text}")
|
|
78
|
+
return []
|
|
79
|
+
json_resp = response.json()
|
|
80
|
+
|
|
81
|
+
items = []
|
|
82
|
+
if isinstance(json_resp, dict):
|
|
83
|
+
if "data" in json_resp and isinstance(json_resp["data"], list):
|
|
84
|
+
items = json_resp["data"]
|
|
85
|
+
elif "results" in json_resp:
|
|
86
|
+
items = json_resp["results"]
|
|
87
|
+
elif isinstance(json_resp, list):
|
|
88
|
+
items = json_resp
|
|
89
|
+
if not items:
|
|
90
|
+
return []
|
|
91
|
+
results = items[:self.top_k]
|
|
92
|
+
clean_results = []
|
|
93
|
+
for item in results:
|
|
94
|
+
entry = {
|
|
95
|
+
"title": item.get("title", "No Title"),
|
|
96
|
+
"url": item.get("url", ""),
|
|
97
|
+
"description": item.get("description", ""),
|
|
98
|
+
"content": item.get("content", ""),
|
|
99
|
+
}
|
|
100
|
+
if self.with_links_summary and "links" in item:
|
|
101
|
+
entry["links"] = item["links"]
|
|
102
|
+
|
|
103
|
+
clean_results.append(entry)
|
|
104
|
+
|
|
105
|
+
return clean_results
|
|
106
|
+
|
|
107
|
+
except requests.exceptions.Timeout:
|
|
108
|
+
print("Error: Search request timed out.")
|
|
109
|
+
return []
|
|
110
|
+
except requests.exceptions.RequestException as e:
|
|
111
|
+
print(f"Error calling Jina API: {e}")
|
|
112
|
+
return []
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"Unexpected error: {e}")
|
|
115
|
+
return []
|
|
116
|
+
|
|
117
|
+
def to_string_context(self) -> str:
|
|
118
|
+
results = self.__call__()
|
|
119
|
+
if not results:
|
|
120
|
+
return "No search results found."
|
|
121
|
+
|
|
122
|
+
parts = []
|
|
123
|
+
for i, item in enumerate(results, 1):
|
|
124
|
+
# limit content length to avoid overly long context
|
|
125
|
+
content_preview = item['content'][:2000] if item['content'] else item['description']
|
|
126
|
+
|
|
127
|
+
parts.append(
|
|
128
|
+
f"### Result {i}\n"
|
|
129
|
+
f"**Title**: {item['title']}\n"
|
|
130
|
+
f"**Source**: {item['url']}\n"
|
|
131
|
+
f"**Content**:\n{content_preview}\n"
|
|
132
|
+
)
|
|
133
|
+
return "\n\n".join(parts)
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
import os
|
|
137
|
+
JINA_API_KEY = os.getenv("JINA_API_KEY")
|
|
138
|
+
search_query = "DeepSeek-V3 的技术架构特点"
|
|
139
|
+
search_node = WebSearchJinaComponent(
|
|
140
|
+
api_key=JINA_API_KEY,
|
|
141
|
+
query=search_query,
|
|
142
|
+
top_k=3
|
|
143
|
+
)
|
|
144
|
+
print(f"正在搜索: {search_query} ...")
|
|
145
|
+
search_context = search_node.to_string_context()
|
|
146
|
+
print("搜索完成,部分上下文预览:")
|
|
147
|
+
print(search_context + "...\n")
|
alayaflow/workflow/__init__.py
CHANGED
|
@@ -2,5 +2,9 @@ from alayaflow.workflow.workflow_info import WorkflowInfo
|
|
|
2
2
|
from alayaflow.workflow.workflow_loader import WorkflowLoader
|
|
3
3
|
from alayaflow.workflow.workflow_manager import WorkflowManager
|
|
4
4
|
|
|
5
|
-
__all__ = [
|
|
5
|
+
__all__ = [
|
|
6
|
+
"WorkflowInfo",
|
|
7
|
+
"WorkflowLoader",
|
|
8
|
+
"WorkflowManager",
|
|
9
|
+
]
|
|
6
10
|
|
|
@@ -105,8 +105,11 @@ class WorkflowLoader:
|
|
|
105
105
|
|
|
106
106
|
return get_input_schema_func, create_graph_func
|
|
107
107
|
|
|
108
|
-
def load_workflow(self, init_args: dict =
|
|
108
|
+
def load_workflow(self, init_args: dict = None, force_reload: bool = False) -> BaseRunnableWorkflow:
|
|
109
109
|
"""加载工作流,返回包含 info 和 creator 的加载结果"""
|
|
110
|
+
if init_args is None:
|
|
111
|
+
init_args = {}
|
|
112
|
+
|
|
110
113
|
info = self.load_info()
|
|
111
114
|
|
|
112
115
|
# now we only support StateGraph
|
|
@@ -116,12 +119,14 @@ class WorkflowLoader:
|
|
|
116
119
|
|
|
117
120
|
return runnable
|
|
118
121
|
|
|
119
|
-
|
|
122
|
+
@classmethod
|
|
123
|
+
def _sanitize_module_name(cls, wf_id: str, version: str) -> str:
|
|
120
124
|
safe_version = version.replace(".", "_")
|
|
121
125
|
safe_wf_id = re.sub(r'[^0-9a-zA-Z_]', '_', wf_id)
|
|
122
126
|
return f"workflow.{safe_wf_id}.{safe_version}"
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def _load_module(cls, module_name: str, filepath: Path) -> Any:
|
|
125
130
|
"""加载 Python 模块,支持相对导入"""
|
|
126
131
|
try:
|
|
127
132
|
# 工作流目录(包含 workflow.py 的目录)
|
|
@@ -151,7 +156,8 @@ class WorkflowLoader:
|
|
|
151
156
|
del sys.modules[module_name]
|
|
152
157
|
raise ImportError(f"加载工作流模块失败: {e}") from e
|
|
153
158
|
|
|
154
|
-
|
|
159
|
+
@classmethod
|
|
160
|
+
def _resolve_entry_point(cls, module: Any, entry_point: str, workflow_id: str) -> Any:
|
|
155
161
|
parts = entry_point.split('.')
|
|
156
162
|
obj = module
|
|
157
163
|
|
|
@@ -164,5 +170,3 @@ class WorkflowLoader:
|
|
|
164
170
|
obj = getattr(obj, part)
|
|
165
171
|
|
|
166
172
|
return obj
|
|
167
|
-
|
|
168
|
-
|
|
@@ -4,7 +4,7 @@ import shutil
|
|
|
4
4
|
import tempfile
|
|
5
5
|
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
8
|
import requests
|
|
9
9
|
|
|
10
10
|
from alayaflow.workflow.workflow_info import WorkflowKey, WorkflowInfo
|
|
@@ -24,11 +24,23 @@ class WorkflowManager:
|
|
|
24
24
|
self._workflow_cache.clear()
|
|
25
25
|
self._requirements_cache.clear()
|
|
26
26
|
|
|
27
|
-
def load_workflow(self, workflow_id: str, version: str, init_args:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
def load_workflow(self, workflow_id: str, version: str, init_args: Optional[Dict] = None, storage_path: str = settings.workflow_storage_path) -> None:
|
|
28
|
+
if init_args is None:
|
|
29
|
+
init_args = {}
|
|
30
|
+
loader = WorkflowLoader(storage_path, workflow_id, version)
|
|
31
|
+
runnable = loader.load_workflow(init_args)
|
|
32
|
+
requirements = loader.load_requirements()
|
|
33
|
+
self._register_workflow(runnable, requirements)
|
|
34
|
+
|
|
35
|
+
def register_workflow(self, runnable: BaseRunnableWorkflow, requirements: List[str] = None) -> None:
|
|
36
|
+
self._register_workflow( runnable, requirements or [])
|
|
37
|
+
|
|
38
|
+
def _register_workflow(self, runnable: BaseRunnableWorkflow, requirements: List[str]) -> None:
|
|
39
|
+
info = runnable.info
|
|
40
|
+
key = WorkflowKey(info.id, info.version)
|
|
41
|
+
self._info_cache[key] = info
|
|
42
|
+
self._workflow_cache[key] = runnable
|
|
43
|
+
self._requirements_cache[key] = requirements
|
|
32
44
|
|
|
33
45
|
# Keep for future reference
|
|
34
46
|
# def load_workflows(self) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
alayaflow/__init__.py,sha256=
|
|
1
|
+
alayaflow/__init__.py,sha256=_EoEy39ORwhkwio9UhypCa7wCJSLmxv6reHH1Aqt5Kw,121
|
|
2
2
|
alayaflow/api/__init__.py,sha256=y6nWgqC3jhOffTqixKlv3OU_NEAFxbzHSvwJrE5bHNs,187
|
|
3
|
-
alayaflow/api/api_singleton.py,sha256=
|
|
3
|
+
alayaflow/api/api_singleton.py,sha256=SvWeLHPYHT1ABACwmoNj2dWrFjpqLCpSMz1-KW3hsZQ,11640
|
|
4
4
|
alayaflow/clients/alayamem/base_client.py,sha256=pyU2WF2jqNEgBEe8JOZSg13gHQ2pJcBgJ_6YP-5mWkw,540
|
|
5
5
|
alayaflow/clients/alayamem/http_client.py,sha256=n0hAh_ddzEwFfNMsdw5s2dqvuMsyZNGOT-RcHsIXuEw,2171
|
|
6
6
|
alayaflow/common/config.py,sha256=pi4zH_Pi0u6Fb8ZIs4u3qFOUOUeqxxUqqksoUp-hynM,3806
|
|
@@ -10,6 +10,7 @@ alayaflow/component/intent_classifier.py,sha256=5KH52LIqIDpw2hlX4gi3Ff7SFVhenCFd
|
|
|
10
10
|
alayaflow/component/llm_node.py,sha256=rvj1f8gqKmdGZkM8Mo8z1PDafdpxhn52nT8L19ZxmaI,3510
|
|
11
11
|
alayaflow/component/memory.py,sha256=Xl5ABW89dswC9oMwkeS6NFAZniKFw8BuGuLAAHddRRA,1448
|
|
12
12
|
alayaflow/component/retrieve_node.py,sha256=mPzAiAXMCGWFYLnmU7kHuf9ejUhzpE_QSHJwc7LOlAU,451
|
|
13
|
+
alayaflow/component/search_node.py,sha256=JNFD6qXDd2_NPpXQf7z8xklJFZs7UrFhWet43nrs25Y,4950
|
|
13
14
|
alayaflow/component/web_search.py,sha256=HZp9j0X0YMBC_mhGqzi0g0pbvmlWiLiNdRxzbEFzl6s,3403
|
|
14
15
|
alayaflow/component/langflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
16
|
alayaflow/component/langflow/intent_classifier.py,sha256=0xqq2wpVVnEErgjOenKYHMsXMkCrHOlCpEZRJqw3PoM,2822
|
|
@@ -17,7 +18,7 @@ alayaflow/component/model/__init__.py,sha256=v63w90tlcAIEy6cN0wTvnvx3afqwNYSn_fW
|
|
|
17
18
|
alayaflow/component/model/model_manager.py,sha256=VTqkCLXxhGqSXi7GcBxaRHw3wTTfEk1rP31UMYvPW7Y,2503
|
|
18
19
|
alayaflow/component/model/schemas.py,sha256=YADx6OGltGMzpUSNEYcUbGOrrJvtV6dFdAub3FfAw9w,1404
|
|
19
20
|
alayaflow/execution/__init__.py,sha256=9gj_xgIJxlq3EuwsDMb5X05yP70HoD0_T25Khd495Xc,117
|
|
20
|
-
alayaflow/execution/env_manager.py,sha256
|
|
21
|
+
alayaflow/execution/env_manager.py,sha256=-7npm1f-FNhHyOt8NZGbGA3i7JMHqQXoWV1uAe1bpyE,16744
|
|
21
22
|
alayaflow/execution/executor_manager.py,sha256=_t_tr58yUsMbIW_xfevUXv8ba-7lGbBJFhjPsQ-ricM,2147
|
|
22
23
|
alayaflow/execution/langfuse_tracing.py,sha256=BuRMHDH7Gub7CMkJM5ECLzs4vjy3VqAgzh2INE9zbOI,3882
|
|
23
24
|
alayaflow/execution/workflow_runner.py,sha256=XEX4Em0Hv1sI8Im0lREjXq3fN1jYVwFnMMW3pphIAZk,3243
|
|
@@ -27,14 +28,14 @@ alayaflow/execution/executors/naive_executor.py,sha256=ICXslitv-9ONvJ3kLC-3-vTTB
|
|
|
27
28
|
alayaflow/execution/executors/uv_executor.py,sha256=IIwP4j-BuDmfNoizuLsHcW0hRmzsLArWRVtVToX3_dM,4622
|
|
28
29
|
alayaflow/execution/executors/worker_executor.py,sha256=niyTqsxB1iHvkuYb3xd35UwnsQllKul-Z6ikenJZ9Hk,513
|
|
29
30
|
alayaflow/utils/singleton.py,sha256=5crFVfOkr9pU_j83ywqAMaL07BvVN5Ke_VGjT9qyUN0,432
|
|
30
|
-
alayaflow/workflow/__init__.py,sha256=
|
|
31
|
+
alayaflow/workflow/__init__.py,sha256=mzqmL4P7q7ixTp2b0rZE-5iEBh7vv4YaTyvqnQZKmos,267
|
|
31
32
|
alayaflow/workflow/workflow_info.py,sha256=rnpAwYE4trhiv7o8LPmQpyQ3CDFfNN2yk1CLKRnWz0w,1259
|
|
32
|
-
alayaflow/workflow/workflow_loader.py,sha256=
|
|
33
|
-
alayaflow/workflow/workflow_manager.py,sha256=
|
|
33
|
+
alayaflow/workflow/workflow_loader.py,sha256=0EP_SEgcGyvSKgWF3DDL4CEmy5NcxRyCpFV5yXzFviM,6729
|
|
34
|
+
alayaflow/workflow/workflow_manager.py,sha256=dUFS6B5V64mdsopxrM6f3LvJn497eTBqMJaXokypBkI,11980
|
|
34
35
|
alayaflow/workflow/runnable/__init__.py,sha256=sNybFeRxLwbDLHiZxlVFXsn3w2n1Jn0Mtun2W6fvjFU,257
|
|
35
36
|
alayaflow/workflow/runnable/base_runnable_workflow.py,sha256=ap53fOeC5iUh2zm45LpEDjLJ4uqfO2C6FCN6WGm13kw,776
|
|
36
37
|
alayaflow/workflow/runnable/state_graph_runnable_workflow.py,sha256=PMSHks46kmNM2uDVmf5TNcLW7AR6dgfJFohxs8Dcfm4,972
|
|
37
|
-
alayaflow-0.1.
|
|
38
|
-
alayaflow-0.1.
|
|
39
|
-
alayaflow-0.1.
|
|
40
|
-
alayaflow-0.1.
|
|
38
|
+
alayaflow-0.1.2.dist-info/METADATA,sha256=CMWzmY6f6eWQNd6l8vC-PXnfIsSpoyDaNP18vlEGZZo,1925
|
|
39
|
+
alayaflow-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
40
|
+
alayaflow-0.1.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
41
|
+
alayaflow-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|