flowllm 0.1.3__py3-none-any.whl → 0.1.5__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 (81) hide show
  1. flowllm/__init__.py +4 -3
  2. flowllm/app.py +1 -1
  3. flowllm/config/base.yaml +75 -0
  4. flowllm/config/fin_supply.yaml +39 -0
  5. flowllm/config/pydantic_config_parser.py +16 -1
  6. flowllm/context/__init__.py +2 -0
  7. flowllm/context/base_context.py +10 -20
  8. flowllm/context/flow_context.py +45 -2
  9. flowllm/context/service_context.py +69 -10
  10. flowllm/embedding_model/openai_compatible_embedding_model.py +1 -2
  11. flowllm/enumeration/chunk_enum.py +1 -0
  12. flowllm/flow/__init__.py +9 -0
  13. flowllm/flow/base_flow.py +44 -13
  14. flowllm/flow/expression/__init__.py +1 -0
  15. flowllm/flow/{parser → expression}/expression_parser.py +5 -2
  16. flowllm/flow/expression/expression_tool_flow.py +25 -0
  17. flowllm/flow/gallery/__init__.py +1 -8
  18. flowllm/flow/gallery/mock_tool_flow.py +46 -28
  19. flowllm/flow/tool_op_flow.py +97 -0
  20. flowllm/llm/base_llm.py +0 -2
  21. flowllm/op/__init__.py +3 -4
  22. flowllm/op/akshare/get_ak_a_code_op.py +1 -1
  23. flowllm/op/akshare/get_ak_a_info_op.py +1 -1
  24. flowllm/op/base_op.py +232 -16
  25. flowllm/op/base_tool_op.py +47 -0
  26. flowllm/op/gallery/__init__.py +0 -1
  27. flowllm/op/gallery/mock_op.py +13 -7
  28. flowllm/op/llm/__init__.py +3 -0
  29. flowllm/op/{agent/react_v2_op.py → llm/react_llm_op.py} +43 -24
  30. flowllm/op/llm/simple_llm_op.py +48 -0
  31. flowllm/op/llm/stream_llm_op.py +61 -0
  32. flowllm/op/mcp/__init__.py +2 -0
  33. flowllm/op/mcp/ant_op.py +42 -0
  34. flowllm/op/mcp/base_sse_mcp_op.py +28 -0
  35. flowllm/op/parallel_op.py +5 -1
  36. flowllm/op/search/__init__.py +1 -2
  37. flowllm/op/search/dashscope_search_op.py +73 -128
  38. flowllm/op/search/tavily_search_op.py +64 -82
  39. flowllm/op/sequential_op.py +4 -0
  40. flowllm/schema/flow_stream_chunk.py +11 -0
  41. flowllm/schema/service_config.py +8 -3
  42. flowllm/schema/tool_call.py +46 -1
  43. flowllm/service/__init__.py +0 -1
  44. flowllm/service/base_service.py +31 -14
  45. flowllm/service/http_service.py +45 -36
  46. flowllm/service/mcp_service.py +17 -23
  47. flowllm/storage/vector_store/__init__.py +1 -0
  48. flowllm/storage/vector_store/base_vector_store.py +99 -15
  49. flowllm/storage/vector_store/chroma_vector_store.py +250 -8
  50. flowllm/storage/vector_store/es_vector_store.py +288 -32
  51. flowllm/storage/vector_store/local_vector_store.py +206 -9
  52. flowllm/storage/vector_store/memory_vector_store.py +509 -0
  53. flowllm/utils/common_utils.py +54 -0
  54. flowllm/utils/miner_u_pdf_processor.py +726 -0
  55. {flowllm-0.1.3.dist-info → flowllm-0.1.5.dist-info}/METADATA +7 -6
  56. flowllm-0.1.5.dist-info/RECORD +98 -0
  57. flowllm/config/default.yaml +0 -77
  58. flowllm/config/empty.yaml +0 -37
  59. flowllm/flow/gallery/cmd_flow.py +0 -11
  60. flowllm/flow/gallery/code_tool_flow.py +0 -30
  61. flowllm/flow/gallery/dashscope_search_tool_flow.py +0 -34
  62. flowllm/flow/gallery/deepsearch_tool_flow.py +0 -39
  63. flowllm/flow/gallery/expression_tool_flow.py +0 -18
  64. flowllm/flow/gallery/tavily_search_tool_flow.py +0 -30
  65. flowllm/flow/gallery/terminate_tool_flow.py +0 -30
  66. flowllm/flow/parser/__init__.py +0 -0
  67. flowllm/op/agent/__init__.py +0 -1
  68. flowllm/op/agent/react_v1_op.py +0 -109
  69. flowllm/op/agent/react_v1_prompt.yaml +0 -54
  70. flowllm/op/base_ray_op.py +0 -313
  71. flowllm/op/code/__init__.py +0 -1
  72. flowllm/op/code/execute_code_op.py +0 -42
  73. flowllm/op/gallery/terminate_op.py +0 -29
  74. flowllm/op/search/dashscope_deep_research_op.py +0 -267
  75. flowllm/service/cmd_service.py +0 -15
  76. flowllm-0.1.3.dist-info/RECORD +0 -102
  77. /flowllm/op/{agent/react_v2_prompt.yaml → llm/react_llm_prompt.yaml} +0 -0
  78. {flowllm-0.1.3.dist-info → flowllm-0.1.5.dist-info}/WHEEL +0 -0
  79. {flowllm-0.1.3.dist-info → flowllm-0.1.5.dist-info}/entry_points.txt +0 -0
  80. {flowllm-0.1.3.dist-info → flowllm-0.1.5.dist-info}/licenses/LICENSE +0 -0
  81. {flowllm-0.1.3.dist-info → flowllm-0.1.5.dist-info}/top_level.txt +0 -0
flowllm/op/base_ray_op.py DELETED
@@ -1,313 +0,0 @@
1
- from abc import ABC
2
-
3
- import pandas as pd
4
- import ray
5
- from loguru import logger
6
- from tqdm import tqdm
7
-
8
- from flowllm.context.service_context import C
9
- from flowllm.op.base_op import BaseOp
10
-
11
-
12
- class BaseRayOp(BaseOp, ABC):
13
- """
14
- Base class for Ray-based operations that provides parallel task execution capabilities.
15
- Inherits from BaseOp and provides methods for submitting and joining Ray tasks.
16
- """
17
-
18
- def submit_and_join_ray_task(self, fn, parallel_key: str = "", task_desc: str = "",
19
- enable_test: bool = False, **kwargs):
20
- """
21
- Submit multiple Ray tasks in parallel and wait for all results.
22
-
23
- This method automatically detects a list parameter to parallelize over, distributes
24
- the work across multiple Ray workers, and returns the combined results.
25
-
26
- Args:
27
- fn: Function to execute in parallel
28
- parallel_key: Key of the parameter to parallelize over (auto-detected if empty)
29
- task_desc: Description for logging and progress bars
30
- enable_test: Enable test mode (prints results instead of executing)
31
- **kwargs: Arguments to pass to the function, including the list to parallelize over
32
-
33
- Returns:
34
- List of results from all parallel tasks
35
- """
36
- max_workers = C.service_config.ray_max_workers
37
- self.ray_task_list.clear()
38
-
39
- # Auto-detect parallel key if not provided
40
- if not parallel_key:
41
- for key, value in kwargs.items():
42
- if isinstance(value, list):
43
- parallel_key = key
44
- logger.info(f"using first list parallel_key={parallel_key}")
45
- break
46
-
47
- # Extract the list to parallelize over
48
- parallel_list = kwargs.pop(parallel_key)
49
- assert isinstance(parallel_list, list)
50
-
51
- # Convert pandas DataFrames to Ray objects for efficient sharing
52
- for key in sorted(kwargs.keys()):
53
- value = kwargs[key]
54
- if isinstance(value, pd.DataFrame):
55
- kwargs[key] = ray.put(value)
56
-
57
- if enable_test:
58
- test_result_list = []
59
- for value in parallel_list:
60
- kwargs.update({"actor_index": 0, parallel_key: value})
61
- t_result = fn(**kwargs)
62
- if t_result:
63
- if isinstance(t_result, list):
64
- test_result_list.extend(t_result)
65
- else:
66
- test_result_list.append(t_result)
67
- return test_result_list
68
-
69
- # Create and submit tasks for each worker
70
- for i in range(max_workers):
71
- def fn_wrapper():
72
- result_list = []
73
- # Distribute work using stride: worker i-th processes items [i, i+max_workers, i+2*max_workers, ...]
74
- for parallel_value in parallel_list[i::max_workers]:
75
- kwargs.update({
76
- "actor_index": i,
77
- parallel_key: parallel_value,
78
- })
79
- part_result = fn(**kwargs)
80
- if part_result:
81
- if isinstance(part_result, list):
82
- result_list.extend(part_result)
83
- else:
84
- result_list.append(part_result)
85
- return result_list
86
-
87
- self.submit_ray_task(fn=fn_wrapper)
88
- logger.info(f"ray.submit task_desc={task_desc} id={i}")
89
-
90
- # Wait for all tasks to complete and collect results
91
- result = self.join_ray_task(task_desc=task_desc)
92
- logger.info(f"{task_desc} complete. result_size={len(result)} resources={ray.available_resources()}")
93
- return result
94
-
95
- def submit_ray_task(self, fn, *args, **kwargs):
96
- """
97
- Submit a single Ray task for asynchronous execution.
98
-
99
- Args:
100
- fn: Function to execute remotely
101
- *args: Positional arguments for the function
102
- **kwargs: Keyword arguments for the function
103
-
104
- Returns:
105
- Self for method chaining
106
-
107
- Raises:
108
- RuntimeError: If Ray is not configured (ray_max_workers <= 1)
109
- """
110
- if C.service_config.ray_max_workers <= 1:
111
- raise RuntimeError("Ray is not configured. Please set ray_max_workers > 1 in service config.")
112
-
113
- # Initialize Ray if not already done
114
- if not ray.is_initialized():
115
- logger.warning(f"Ray is not initialized. Initializing Ray with {C.service_config.ray_max_workers} workers.")
116
- ray.init(num_cpus=C.service_config.ray_max_workers)
117
-
118
- # Create remote function and submit task
119
- remote_fn = ray.remote(fn)
120
- task = remote_fn.remote(*args, **kwargs)
121
- self.ray_task_list.append(task)
122
- return self
123
-
124
- def join_ray_task(self, task_desc: str = None) -> list:
125
- """
126
- Wait for all submitted Ray tasks to complete and collect their results.
127
-
128
- Args:
129
- task_desc: Description for the progress bar
130
-
131
- Returns:
132
- Combined list of results from all completed tasks
133
- """
134
- result = []
135
- # Process each task and collect results with progress bar
136
- for task in tqdm(self.ray_task_list, desc=task_desc or f"{self.name}_ray"):
137
- t_result = ray.get(task)
138
- if t_result:
139
- if isinstance(t_result, list):
140
- result.extend(t_result)
141
- else:
142
- result.append(t_result)
143
- self.ray_task_list.clear()
144
- return result
145
-
146
-
147
- def run():
148
- """Test Ray multiprocessing functionality"""
149
- import time
150
- import math
151
-
152
- # CPU intensive task for testing
153
- def cpu_intensive_task(n: int, task_id: str):
154
- """CPU intensive task: calculate prime numbers"""
155
- start_t = time.time()
156
-
157
- def is_prime(num):
158
- if num < 2:
159
- return False
160
- for j in range(2, int(math.sqrt(num)) + 1):
161
- if num % j == 0:
162
- return False
163
- return True
164
-
165
- primes = [x for x in range(2, n) if is_prime(x)]
166
- end_t = time.time()
167
-
168
- result = {
169
- 'task_id': task_id,
170
- 'prime_count': len(primes),
171
- 'max_prime': max(primes) if primes else 0,
172
- 'execution_time': end_t - start_t
173
- }
174
- logger.info(f"Task {task_id} completed: found {len(primes)} primes, time: {result['execution_time']:.2f}s")
175
- return result
176
-
177
- class TestRayOp(BaseRayOp):
178
- def execute(self):
179
- logger.info(f"Executing {self.name}")
180
- return f"Result from {self.name}"
181
-
182
- # Initialize service config for Ray
183
- from flowllm.schema.service_config import ServiceConfig
184
-
185
- # Create a test service config with Ray enabled
186
- test_config = ServiceConfig()
187
- test_config.ray_max_workers = 4 # Enable Ray with 4 workers
188
- test_config.thread_pool_max_workers = 4
189
-
190
- # Set the service config
191
- C.init_by_service_config(test_config)
192
-
193
- logger.info("=== Testing Ray multiprocessing ===")
194
-
195
- # Create test operation
196
- ray_op = TestRayOp("ray_test_op")
197
-
198
- logger.info("--- Testing submit_ray_task and join_ray_task ---")
199
-
200
- # Test 1: Basic Ray task submission
201
- task_size = 50000 # Find primes up to 50000 (more CPU intensive)
202
- num_tasks = 4
203
-
204
- try:
205
- # Submit multiple CPU-intensive tasks
206
-
207
- logger.info(f"Submitting {num_tasks} Ray tasks (finding primes up to {task_size})")
208
- start_time = time.time()
209
-
210
- for i in range(num_tasks):
211
- ray_op.submit_ray_task(cpu_intensive_task, task_size, f"ray_task_{i}")
212
-
213
- # Wait for all tasks to complete
214
- results = ray_op.join_ray_task("Processing Ray tasks")
215
- end_time = time.time()
216
-
217
- logger.info(f"Ray tasks completed in {end_time - start_time:.2f}s")
218
- logger.info(f"Ray results: {results}")
219
-
220
- except Exception as e:
221
- logger.error(f"Ray task execution failed: {e}")
222
-
223
- # Test 2: Compare Ray vs ThreadPool performance
224
- logger.info("\n--- Performance Comparison: Ray vs ThreadPool ---")
225
-
226
- try:
227
- # Test with ThreadPool
228
- thread_op = TestRayOp("thread_test_op")
229
-
230
- logger.info(f"Testing ThreadPool with {num_tasks} tasks")
231
- start_time = time.time()
232
-
233
- for i in range(num_tasks):
234
- thread_op.submit_task(cpu_intensive_task, task_size, f"thread_task_{i}")
235
-
236
- thread_results = thread_op.join_task("Processing ThreadPool tasks")
237
- print(thread_results)
238
- thread_time = time.time() - start_time
239
-
240
- logger.info(f"ThreadPool completed in {thread_time:.2f}s")
241
-
242
- # Test with Ray again for comparison
243
- ray_op2 = TestRayOp("ray_test_op2")
244
-
245
- logger.info(f"Testing Ray with {num_tasks} tasks")
246
- start_time = time.time()
247
-
248
- for i in range(num_tasks):
249
- ray_op2.submit_ray_task(cpu_intensive_task, task_size, f"ray_task2_{i}")
250
-
251
- ray_results2 = ray_op2.join_ray_task("Processing Ray tasks (comparison)")
252
- print(ray_results2)
253
- ray_time = time.time() - start_time
254
-
255
- logger.info(f"Ray completed in {ray_time:.2f}s")
256
-
257
- # Performance comparison
258
- speedup = thread_time / ray_time if ray_time > 0 else 0
259
- logger.info(f"\n=== Performance Summary ===")
260
- logger.info(f"ThreadPool time: {thread_time:.2f}s")
261
- logger.info(f"Ray time: {ray_time:.2f}s")
262
- logger.info(f"Ray speedup: {speedup:.2f}x")
263
-
264
- except Exception as e:
265
- logger.error(f"Performance comparison failed: {e}")
266
-
267
- # Test 3: Error handling
268
- logger.info("\n--- Testing Error Handling ---")
269
-
270
- def failing_task(task_id: str):
271
- if task_id == "fail_task":
272
- raise ValueError(f"Intentional error in {task_id}")
273
- return f"Success: {task_id}"
274
-
275
- try:
276
- error_op = TestRayOp("error_test_op")
277
-
278
- # Submit mix of successful and failing tasks
279
- error_op.submit_ray_task(failing_task, "success_task_1")
280
- error_op.submit_ray_task(failing_task, "fail_task")
281
- error_op.submit_ray_task(failing_task, "success_task_2")
282
-
283
- error_results = error_op.join_ray_task("Testing error handling")
284
- logger.info(f"Error handling results: {error_results}")
285
-
286
- except Exception as e:
287
- logger.error(f"Expected error occurred: {e}")
288
-
289
- # Test 4: Ray without proper configuration (should fail)
290
- logger.info("\n--- Testing Ray Configuration Validation ---")
291
-
292
- original_workers = C.service_config.ray_max_workers
293
- try:
294
- # Temporarily disable Ray in config
295
- C.service_config.ray_max_workers = 1 # Disable Ray
296
-
297
- config_test_op = TestRayOp("config_test_op")
298
- config_test_op.submit_ray_task(cpu_intensive_task, 100, "config_test")
299
-
300
- logger.error("This should not be reached - Ray should be disabled")
301
-
302
- except RuntimeError as e:
303
- logger.info(f"✓ Correctly caught configuration error: {e}")
304
-
305
- finally:
306
- # Restore original configuration
307
- C.service_config.ray_max_workers = original_workers
308
-
309
- logger.info("\n=== Ray testing completed ===")
310
-
311
-
312
- if __name__ == "__main__":
313
- run()
@@ -1 +0,0 @@
1
- from .execute_code_op import ExecuteCodeOp
@@ -1,42 +0,0 @@
1
- import sys
2
- from io import StringIO
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.op.base_op import BaseOp
9
-
10
-
11
- @C.register_op()
12
- class ExecuteCodeOp(BaseOp):
13
-
14
- def execute(self):
15
- old_stdout = sys.stdout
16
- redirected_output = sys.stdout = StringIO()
17
-
18
- try:
19
- code_key: str = self.op_params.get("code_key", "code")
20
- code_str: str = self.context[code_key]
21
- exec(code_str)
22
- code_result = redirected_output.getvalue()
23
-
24
- except Exception as e:
25
- logger.info(f"{self.name} encounter exception! error={e.args}")
26
- code_result = str(e)
27
-
28
- sys.stdout = old_stdout
29
- self.context.code_result = code_result
30
-
31
-
32
- if __name__ == "__main__":
33
- C.set_default_service_config().init_by_service_config()
34
- op = ExecuteCodeOp()
35
-
36
- context = FlowContext(code="print('Hello World')")
37
- op(context=context)
38
- print(context.code_result)
39
-
40
- context.code = "print('Hello World!'"
41
- op(context=context)
42
- print(context.code_result)
@@ -1,29 +0,0 @@
1
- from flowllm.context.service_context import C
2
- from flowllm.op.base_op import BaseOp
3
-
4
-
5
- @C.register_op()
6
- class TerminateOp(BaseOp):
7
-
8
- def execute(self):
9
- # Get status from context
10
- status = self.context.status
11
- assert status in ["success", "failure"], f"Invalid status: {status}"
12
- self.context.terminate_answer = f"The interaction has been completed with status: {status}"
13
-
14
-
15
- if __name__ == "__main__":
16
- from flowllm.context.flow_context import FlowContext
17
-
18
- C.set_default_service_config().init_by_service_config()
19
-
20
- # Test success termination
21
- op = TerminateOp()
22
- context = FlowContext(status="success")
23
- result = op(context=context)
24
- print(f"Result: {context.terminate_answer}")
25
-
26
- # Test failure termination
27
- context.status = "failure"
28
- op(context=context)
29
- print(f"Result: {context.terminate_answer}")
@@ -1,267 +0,0 @@
1
- import os
2
-
3
- import dashscope
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.op.base_llm_op import BaseLLMOp
9
- from flowllm.storage.cache.data_cache import DataCache
10
-
11
-
12
- @C.register_op()
13
- class DashscopeDeepResearchOp(BaseLLMOp):
14
- file_path: str = __file__
15
-
16
- """
17
- Dashscope deep research operation using Alibaba's Qwen-deep-research model.
18
-
19
- This operation performs deep research using Dashscope's Generation API with the
20
- qwen-deep-research model. It handles the multi-phase research process including
21
- model questioning, web research, and result generation.
22
- """
23
-
24
- def __init__(self,
25
- model: str = "qwen-deep-research",
26
- enable_print: bool = True,
27
- enable_cache: bool = True,
28
- cache_path: str = "./dashscope_deep_research_cache",
29
- cache_expire_hours: float = 24,
30
- max_retries: int = 3,
31
- return_only_content: bool = True,
32
- **kwargs):
33
- super().__init__(**kwargs)
34
-
35
- self.model = model
36
- self.enable_print = enable_print
37
- self.enable_cache = enable_cache
38
- self.cache_expire_hours = cache_expire_hours
39
- self.max_retries = max_retries
40
- self.return_only_content = return_only_content
41
-
42
- # Ensure API key is available
43
- self.api_key = os.environ["FLOW_DASHSCOPE_API_KEY"]
44
- self.cache_path: str = cache_path
45
- self._cache: DataCache | None = None
46
-
47
- @property
48
- def cache(self):
49
- if self.enable_cache and self._cache is None:
50
- self._cache = DataCache(self.cache_path)
51
- return self._cache
52
-
53
- def process_responses(self, responses, step_name):
54
- """Process streaming responses from the deep research model"""
55
- current_phase = None
56
- phase_content = ""
57
- research_goal = ""
58
- web_sites = []
59
- keepalive_shown = False # 标记是否已经显示过KeepAlive提示
60
-
61
- for response in responses:
62
- # 检查响应状态码
63
- if hasattr(response, 'status_code') and response.status_code != 200:
64
- logger.warning(f"HTTP返回码:{response.status_code}")
65
- if hasattr(response, 'code'):
66
- logger.warning(f"错误码:{response.code}")
67
- if hasattr(response, 'message'):
68
- logger.warning(f"错误信息:{response.message}")
69
- continue
70
-
71
- if hasattr(response, 'output') and response.output:
72
- message = response.output.get('message', {})
73
- phase = message.get('phase')
74
- content = message.get('content', '')
75
- status = message.get('status')
76
- extra = message.get('extra', {})
77
-
78
- # 阶段变化检测
79
- if phase != current_phase:
80
- if current_phase and phase_content:
81
- # 根据阶段名称和步骤名称来显示不同的完成描述
82
- if step_name == "第一步:模型反问确认" and current_phase == "answer":
83
- logger.info("模型反问阶段完成")
84
- else:
85
- logger.info(f"{current_phase} 阶段完成")
86
- current_phase = phase
87
- phase_content = ""
88
- keepalive_shown = False # 重置KeepAlive提示标记
89
-
90
- # 根据阶段名称和步骤名称来显示不同的描述
91
- if step_name == "第一步:模型反问确认" and phase == "answer":
92
- logger.info("进入模型反问阶段")
93
- else:
94
- logger.info(f"进入 {phase} 阶段")
95
-
96
- # 处理WebResearch阶段的特殊信息
97
- if phase == "WebResearch":
98
- if extra.get('deep_research', {}).get('research'):
99
- research_info = extra['deep_research']['research']
100
-
101
- # 处理streamingQueries状态
102
- if status == "streamingQueries":
103
- if 'researchGoal' in research_info:
104
- goal = research_info['researchGoal']
105
- if goal:
106
- research_goal += goal
107
- if self.enable_print:
108
- print(f" 研究目标: {goal}", end='', flush=True)
109
-
110
- # 处理streamingWebResult状态
111
- elif status == "streamingWebResult":
112
- if 'webSites' in research_info:
113
- sites = research_info['webSites']
114
- if sites and sites != web_sites: # 避免重复显示
115
- web_sites = sites
116
- if self.enable_print:
117
- print(f" 找到 {len(sites)} 个相关网站:")
118
- for i, site in enumerate(sites, 1):
119
- print(f" {i}. {site.get('title', '无标题')}")
120
- print(f" 描述: {site.get('description', '无描述')[:100]}...")
121
- print(f" URL: {site.get('url', '无链接')}")
122
- if site.get('favicon'):
123
- print(f" 图标: {site['favicon']}")
124
- print()
125
-
126
- # 处理WebResultFinished状态
127
- elif status == "WebResultFinished":
128
- if self.enable_print:
129
- print(f" 网络搜索完成,共找到 {len(web_sites)} 个参考信息源")
130
- if research_goal:
131
- print(f" 研究目标: {research_goal}")
132
-
133
- # 累积内容并显示
134
- if content:
135
- phase_content += content
136
- # 实时显示内容
137
- if self.enable_print:
138
- print(content, end='', flush=True)
139
-
140
- # 显示阶段状态变化
141
- if status and status != "typing":
142
- if self.enable_print:
143
- print(f" 状态: {status}")
144
-
145
- # 显示状态说明
146
- if status == "streamingQueries":
147
- if self.enable_print:
148
- print(" → 正在生成研究目标和搜索查询(WebResearch阶段)")
149
- elif status == "streamingWebResult":
150
- if self.enable_print:
151
- print(" → 正在执行搜索、网页阅读和代码执行(WebResearch阶段)")
152
- elif status == "WebResultFinished":
153
- if self.enable_print:
154
- print(" → 网络搜索阶段完成(WebResearch阶段)")
155
-
156
- # 当状态为finished时,显示token消耗情况
157
- if status == "finished":
158
- if hasattr(response, 'usage') and response.usage:
159
- usage = response.usage
160
- if self.enable_print:
161
- print(f" Token消耗统计:")
162
- print(f" 输入tokens: {usage.get('input_tokens', 0)}")
163
- print(f" 输出tokens: {usage.get('output_tokens', 0)}")
164
- print(f" 请求ID: {response.get('request_id', '未知')}")
165
-
166
- if phase == "KeepAlive":
167
- # 只在第一次进入KeepAlive阶段时显示提示
168
- if not keepalive_shown:
169
- if self.enable_print:
170
- print("当前步骤已经完成,准备开始下一步骤工作")
171
- keepalive_shown = True
172
- continue
173
-
174
- if current_phase and phase_content:
175
- if step_name == "第一步:模型反问确认" and current_phase == "answer":
176
- logger.info("模型反问阶段完成")
177
- else:
178
- logger.info(f"{current_phase} 阶段完成")
179
-
180
- return phase_content
181
-
182
- def call_deep_research_model(self, messages, step_name):
183
- """Call the deep research model with the given messages"""
184
- if self.enable_print:
185
- print(f"\n=== {step_name} ===")
186
-
187
- try:
188
- responses = dashscope.Generation.call(
189
- api_key=self.api_key,
190
- model=self.model,
191
- messages=messages,
192
- # qwen-deep-research模型目前仅支持流式输出
193
- stream=True
194
- # incremental_output=True 使用增量输出请添加此参数
195
- )
196
-
197
- return self.process_responses(responses, step_name)
198
-
199
- except Exception as e:
200
- logger.error(f"调用API时发生错误: {e}")
201
- return ""
202
-
203
- def execute(self):
204
- """Execute the Dashscope deep research operation"""
205
- # Get query from context
206
- query = self.context.query
207
-
208
- # Check cache first
209
- if self.enable_cache and self.cache:
210
- cached_result = self.cache.load(query)
211
- if cached_result:
212
- if self.return_only_content:
213
- self.context.dashscope_deep_research_result = cached_result.get("content", "")
214
- else:
215
- self.context.dashscope_deep_research_result = cached_result
216
- return
217
-
218
- # 第一步:模型反问确认
219
- # 模型会分析用户问题,提出细化问题来明确研究方向
220
- messages = [{'role': 'user', 'content': query}]
221
- step1_content = self.call_deep_research_model(messages, "第一步:模型反问确认")
222
-
223
- # 第二步:深入研究
224
- # 基于第一步的反问内容,模型会执行完整的研究流程
225
- messages = [
226
- {'role': 'user', 'content': query},
227
- {'role': 'assistant', 'content': step1_content}, # 包含模型的反问内容
228
- {'role': 'user', 'content': '帮我生成完整且逻辑性的报告'}
229
- ]
230
-
231
- result_content = self.call_deep_research_model(messages, "第二步:深入研究")
232
-
233
- if self.enable_print:
234
- print(result_content)
235
- print("\n 研究完成!")
236
-
237
- # Prepare final result
238
- final_result = {
239
- "query": query,
240
- "step1_content": step1_content,
241
- "final_result": result_content,
242
- "model": self.model
243
- }
244
-
245
- # Cache the result if enabled
246
- if self.enable_cache and self.cache:
247
- self.cache.save(query, final_result, expire_hours=self.cache_expire_hours)
248
-
249
- # Set context
250
- if self.return_only_content:
251
- self.context.dashscope_deep_research_result = result_content
252
- else:
253
- self.context.dashscope_deep_research_result = final_result
254
-
255
-
256
- def main():
257
- C.set_default_service_config().init_by_service_config()
258
-
259
- op = DashscopeDeepResearchOp(enable_print=True, enable_cache=True)
260
-
261
- context = FlowContext(query="中国电解铝行业值得投资吗,有哪些值得投资的标的,各个标的之间需要对比优劣势")
262
- op(context=context)
263
- print(context.dashscope_deep_research_result)
264
-
265
-
266
- if __name__ == "__main__":
267
- main()
@@ -1,15 +0,0 @@
1
- from loguru import logger
2
-
3
- from flowllm.context.service_context import C
4
- from flowllm.flow.gallery import CmdFlow
5
- from flowllm.service.base_service import BaseService
6
-
7
-
8
- @C.register_service("cmd")
9
- class CmdService(BaseService):
10
-
11
- def __call__(self):
12
- flow = CmdFlow(flow=self.service_config.cmd.flow)
13
- response = flow.__call__(**self.service_config.cmd.params)
14
- if response.answer:
15
- logger.info(f"final_answer={response.answer}")