coze-coding-utils 0.2.3a2__py3-none-any.whl → 0.2.4__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.
- coze_coding_utils/helper/stream_runner.py +138 -23
- coze_coding_utils/log/node_log.py +0 -1
- {coze_coding_utils-0.2.3a2.dist-info → coze_coding_utils-0.2.4.dist-info}/METADATA +1 -1
- {coze_coding_utils-0.2.3a2.dist-info → coze_coding_utils-0.2.4.dist-info}/RECORD +6 -6
- {coze_coding_utils-0.2.3a2.dist-info → coze_coding_utils-0.2.4.dist-info}/WHEEL +0 -0
- {coze_coding_utils-0.2.3a2.dist-info → coze_coding_utils-0.2.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,20 +4,28 @@ import threading
|
|
|
4
4
|
import contextvars
|
|
5
5
|
import logging
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
-
from
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Dict, Iterator, AsyncIterable, Optional, Literal
|
|
8
9
|
from langchain_core.runnables import RunnableConfig
|
|
9
10
|
from langgraph.graph.state import CompiledStateGraph
|
|
10
|
-
from coze_coding_utils.runtime_ctx.context import Context
|
|
11
11
|
from coze_coding_utils.helper.agent_helper import (
|
|
12
12
|
to_stream_input,
|
|
13
|
-
to_client_message,
|
|
14
13
|
agent_iter_server_messages,
|
|
15
14
|
)
|
|
15
|
+
|
|
16
|
+
from coze_coding_utils.error import classify_error
|
|
17
|
+
import asyncio
|
|
18
|
+
import time
|
|
19
|
+
import traceback
|
|
20
|
+
from typing import Any, Dict, AsyncGenerator, Callable
|
|
21
|
+
from coze_coding_utils.runtime_ctx.context import Context
|
|
16
22
|
from coze_coding_utils.messages.server import (
|
|
23
|
+
create_message_end_dict,
|
|
24
|
+
create_message_error_dict,
|
|
17
25
|
MESSAGE_END_CODE_CANCELED,
|
|
18
|
-
create_message_end_dict, create_message_error_dict,
|
|
19
26
|
)
|
|
20
|
-
from coze_coding_utils.
|
|
27
|
+
from coze_coding_utils.helper.agent_helper import to_client_message
|
|
28
|
+
from coze_coding_utils.error.classifier import ErrorClassifier
|
|
21
29
|
|
|
22
30
|
logger = logging.getLogger(__name__)
|
|
23
31
|
|
|
@@ -25,6 +33,14 @@ TIMEOUT_SECONDS = 900
|
|
|
25
33
|
PING_INTERVAL_SECONDS = 30
|
|
26
34
|
|
|
27
35
|
|
|
36
|
+
@dataclass
|
|
37
|
+
class RunOpt:
|
|
38
|
+
workflow_debug: bool = False
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def stream_mode(self) -> Literal["debug", "updates"]:
|
|
42
|
+
return "debug" if self.workflow_debug else "updates"
|
|
43
|
+
|
|
28
44
|
class WorkflowEventType:
|
|
29
45
|
WORKFLOW_START = "workflow_start"
|
|
30
46
|
WORKFLOW_END = "workflow_end"
|
|
@@ -41,16 +57,16 @@ class WorkflowErrorCode:
|
|
|
41
57
|
|
|
42
58
|
class BaseStreamRunner(ABC):
|
|
43
59
|
@abstractmethod
|
|
44
|
-
def stream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context) -> Iterator[Any]:
|
|
60
|
+
def stream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context, run_opt: Optional[RunOpt] = None) -> Iterator[Any]:
|
|
45
61
|
pass
|
|
46
62
|
|
|
47
63
|
@abstractmethod
|
|
48
|
-
async def astream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context) -> AsyncIterable[Any]:
|
|
64
|
+
async def astream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context, run_opt: Optional[RunOpt] = None) -> AsyncIterable[Any]:
|
|
49
65
|
pass
|
|
50
66
|
|
|
51
67
|
|
|
52
68
|
class AgentStreamRunner(BaseStreamRunner):
|
|
53
|
-
def stream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context) -> Iterator[Any]:
|
|
69
|
+
def stream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context, run_opt: Optional[RunOpt] = None) -> Iterator[Any]:
|
|
54
70
|
client_msg, session_id = to_client_message(payload)
|
|
55
71
|
run_config["recursion_limit"] = 100
|
|
56
72
|
run_config["configurable"] = {"thread_id": session_id}
|
|
@@ -95,7 +111,7 @@ class AgentStreamRunner(BaseStreamRunner):
|
|
|
95
111
|
)
|
|
96
112
|
yield end_msg
|
|
97
113
|
|
|
98
|
-
async def astream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context) -> AsyncIterable[Any]:
|
|
114
|
+
async def astream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context, run_opt: Optional[RunOpt] = None) -> AsyncIterable[Any]:
|
|
99
115
|
client_msg, session_id = to_client_message(payload)
|
|
100
116
|
run_config["recursion_limit"] = 100
|
|
101
117
|
run_config["configurable"] = {"thread_id": session_id}
|
|
@@ -188,9 +204,6 @@ class AgentStreamRunner(BaseStreamRunner):
|
|
|
188
204
|
|
|
189
205
|
|
|
190
206
|
class WorkflowStreamRunner(BaseStreamRunner):
|
|
191
|
-
def __init__(self):
|
|
192
|
-
self._node_start_times: Dict[str, float] = {}
|
|
193
|
-
|
|
194
207
|
def _serialize_data(self, data: Any) -> Any:
|
|
195
208
|
if isinstance(data, dict):
|
|
196
209
|
return {k: self._serialize_data(v) for k, v in data.items()}
|
|
@@ -215,7 +228,9 @@ class WorkflowStreamRunner(BaseStreamRunner):
|
|
|
215
228
|
result.update(kwargs)
|
|
216
229
|
return result
|
|
217
230
|
|
|
218
|
-
def stream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context) -> Iterator[Any]:
|
|
231
|
+
def stream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context, run_opt: Optional[RunOpt] = None) -> Iterator[Any]:
|
|
232
|
+
if run_opt is None:
|
|
233
|
+
run_opt = RunOpt()
|
|
219
234
|
run_config["recursion_limit"] = 100
|
|
220
235
|
if "configurable" not in run_config:
|
|
221
236
|
run_config["configurable"] = {}
|
|
@@ -226,21 +241,19 @@ class WorkflowStreamRunner(BaseStreamRunner):
|
|
|
226
241
|
node_start_times: Dict[str, float] = {}
|
|
227
242
|
final_output = {}
|
|
228
243
|
seq = 0
|
|
229
|
-
is_debug = run_config.get("configurable", {}).get("workflow_debug", False)
|
|
230
|
-
stream_mode = "debug" if is_debug else "updates"
|
|
231
244
|
|
|
232
245
|
try:
|
|
233
246
|
seq += 1
|
|
234
247
|
yield (seq, self._build_event(WorkflowEventType.WORKFLOW_START, ctx))
|
|
235
248
|
|
|
236
|
-
for event in graph.stream(payload, stream_mode=stream_mode, config=run_config, context=ctx):
|
|
249
|
+
for event in graph.stream(payload, stream_mode=run_opt.stream_mode, config=run_config, context=ctx):
|
|
237
250
|
current_time = time.time()
|
|
238
251
|
if current_time - last_ping_time >= PING_INTERVAL_SECONDS:
|
|
239
252
|
seq += 1
|
|
240
253
|
yield (seq, self._build_event(WorkflowEventType.PING, ctx))
|
|
241
254
|
last_ping_time = current_time
|
|
242
255
|
|
|
243
|
-
if not
|
|
256
|
+
if not run_opt.workflow_debug:
|
|
244
257
|
if isinstance(event, dict):
|
|
245
258
|
logger.info(f"Debug event: {event}")
|
|
246
259
|
for node_name, node_output in event.items():
|
|
@@ -305,7 +318,9 @@ class WorkflowStreamRunner(BaseStreamRunner):
|
|
|
305
318
|
seq += 1
|
|
306
319
|
yield (seq, self._build_event(WorkflowEventType.ERROR, ctx, code=str(err.code), error_msg=err.message))
|
|
307
320
|
|
|
308
|
-
async def astream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context) -> AsyncIterable[Any]:
|
|
321
|
+
async def astream(self, payload: Dict[str, Any], graph: CompiledStateGraph, run_config: RunnableConfig, ctx: Context, run_opt: Optional[RunOpt] = None) -> AsyncIterable[Any]:
|
|
322
|
+
if run_opt is None:
|
|
323
|
+
run_opt = RunOpt()
|
|
309
324
|
run_config["recursion_limit"] = 100
|
|
310
325
|
if "configurable" not in run_config:
|
|
311
326
|
run_config["configurable"] = {}
|
|
@@ -317,9 +332,7 @@ class WorkflowStreamRunner(BaseStreamRunner):
|
|
|
317
332
|
start_time = time.time()
|
|
318
333
|
cancelled = threading.Event()
|
|
319
334
|
last_ping_time = [start_time]
|
|
320
|
-
|
|
321
|
-
stream_mode = "debug" if is_debug else "updates"
|
|
322
|
-
logger.info(f"Stream mode: {stream_mode}")
|
|
335
|
+
logger.info(f"Stream mode: {run_opt.stream_mode}")
|
|
323
336
|
seq = [0]
|
|
324
337
|
|
|
325
338
|
def producer():
|
|
@@ -333,7 +346,7 @@ class WorkflowStreamRunner(BaseStreamRunner):
|
|
|
333
346
|
seq[0] += 1
|
|
334
347
|
loop.call_soon_threadsafe(q.put_nowait, (seq[0], self._build_event(WorkflowEventType.WORKFLOW_START, ctx)))
|
|
335
348
|
|
|
336
|
-
for event in graph.stream(payload, stream_mode=stream_mode, config=run_config, context=ctx):
|
|
349
|
+
for event in graph.stream(payload, stream_mode=run_opt.stream_mode, config=run_config, context=ctx):
|
|
337
350
|
if cancelled.is_set():
|
|
338
351
|
logger.info(f"Workflow producer cancelled during iteration for run_id: {ctx.run_id}")
|
|
339
352
|
seq[0] += 1
|
|
@@ -352,7 +365,7 @@ class WorkflowStreamRunner(BaseStreamRunner):
|
|
|
352
365
|
loop.call_soon_threadsafe(q.put_nowait, (seq[0], self._build_event(WorkflowEventType.PING, ctx)))
|
|
353
366
|
last_ping_time[0] = current_time
|
|
354
367
|
|
|
355
|
-
if not
|
|
368
|
+
if not run_opt.workflow_debug:
|
|
356
369
|
if isinstance(event, dict):
|
|
357
370
|
for node_name, node_output in event.items():
|
|
358
371
|
logger.info(f"Node output: {node_name}")
|
|
@@ -450,6 +463,108 @@ class WorkflowStreamRunner(BaseStreamRunner):
|
|
|
450
463
|
pass
|
|
451
464
|
|
|
452
465
|
|
|
466
|
+
async def agent_stream_handler(
|
|
467
|
+
payload: Dict[str, Any],
|
|
468
|
+
ctx: Context,
|
|
469
|
+
run_id: str,
|
|
470
|
+
stream_sse_func: Callable,
|
|
471
|
+
sse_event_func: Callable,
|
|
472
|
+
error_classifier: ErrorClassifier,
|
|
473
|
+
register_task_func: Callable[[str, asyncio.Task], None],
|
|
474
|
+
) -> AsyncGenerator[str, None]:
|
|
475
|
+
task = asyncio.current_task()
|
|
476
|
+
if task:
|
|
477
|
+
register_task_func(run_id, task)
|
|
478
|
+
logger.info(f"Registered agent streaming task for run_id: {run_id}")
|
|
479
|
+
|
|
480
|
+
client_msg, _ = to_client_message(payload)
|
|
481
|
+
t0 = time.time()
|
|
482
|
+
|
|
483
|
+
try:
|
|
484
|
+
async for chunk in stream_sse_func(payload, ctx):
|
|
485
|
+
yield chunk
|
|
486
|
+
except asyncio.CancelledError:
|
|
487
|
+
logger.info(f"Agent stream cancelled for run_id: {run_id}")
|
|
488
|
+
end_msg = create_message_end_dict(
|
|
489
|
+
code=MESSAGE_END_CODE_CANCELED,
|
|
490
|
+
message="Stream cancelled by user",
|
|
491
|
+
session_id=client_msg.session_id,
|
|
492
|
+
query_msg_id=client_msg.local_msg_id,
|
|
493
|
+
log_id=ctx.logid,
|
|
494
|
+
time_cost_ms=int((time.time() - t0) * 1000),
|
|
495
|
+
reply_id="",
|
|
496
|
+
sequence_id=1,
|
|
497
|
+
)
|
|
498
|
+
yield sse_event_func(end_msg)
|
|
499
|
+
raise
|
|
500
|
+
except Exception as ex:
|
|
501
|
+
err = error_classifier.classify(ex, {"node_name": "agent_stream", "run_id": run_id})
|
|
502
|
+
logger.error(
|
|
503
|
+
f"Unexpected error in agent_stream: [{err.code}] {err.message}, "
|
|
504
|
+
f"traceback: {traceback.format_exc()}"
|
|
505
|
+
)
|
|
506
|
+
error_msg = create_message_error_dict(
|
|
507
|
+
code=str(err.code),
|
|
508
|
+
message=str(ex),
|
|
509
|
+
session_id=client_msg.session_id,
|
|
510
|
+
query_msg_id=client_msg.local_msg_id,
|
|
511
|
+
log_id=ctx.logid,
|
|
512
|
+
reply_id="",
|
|
513
|
+
sequence_id=1,
|
|
514
|
+
local_msg_id=client_msg.local_msg_id,
|
|
515
|
+
)
|
|
516
|
+
yield sse_event_func(error_msg)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
async def workflow_stream_handler(
|
|
520
|
+
payload: Dict[str, Any],
|
|
521
|
+
ctx: Context,
|
|
522
|
+
run_id: str,
|
|
523
|
+
stream_sse_func: Callable,
|
|
524
|
+
sse_event_func: Callable,
|
|
525
|
+
error_classifier: ErrorClassifier,
|
|
526
|
+
register_task_func: Callable[[str, asyncio.Task], None],
|
|
527
|
+
run_opt: Optional[RunOpt] = None,
|
|
528
|
+
) -> AsyncGenerator[str, None]:
|
|
529
|
+
if run_opt is None:
|
|
530
|
+
run_opt = RunOpt()
|
|
531
|
+
task = asyncio.current_task()
|
|
532
|
+
if task:
|
|
533
|
+
register_task_func(run_id, task)
|
|
534
|
+
logger.info(f"Registered workflow streaming task for run_id: {run_id}")
|
|
535
|
+
|
|
536
|
+
try:
|
|
537
|
+
async for chunk in stream_sse_func(payload, ctx, run_opt):
|
|
538
|
+
yield chunk
|
|
539
|
+
except asyncio.CancelledError:
|
|
540
|
+
logger.info(f"Workflow stream cancelled for run_id: {run_id}")
|
|
541
|
+
cancel_event = {
|
|
542
|
+
"type": "error",
|
|
543
|
+
"timestamp": int(time.time() * 1000),
|
|
544
|
+
"log_id": ctx.logid,
|
|
545
|
+
"run_id": run_id,
|
|
546
|
+
"code": "CANCELED",
|
|
547
|
+
"error_msg": "Stream cancelled by user",
|
|
548
|
+
}
|
|
549
|
+
yield sse_event_func(cancel_event)
|
|
550
|
+
raise
|
|
551
|
+
except Exception as ex:
|
|
552
|
+
err = error_classifier.classify(ex, {"node_name": "workflow_stream", "run_id": run_id})
|
|
553
|
+
logger.error(
|
|
554
|
+
f"Unexpected error in workflow_stream: [{err.code}] {err.message}, "
|
|
555
|
+
f"traceback: {traceback.format_exc()}"
|
|
556
|
+
)
|
|
557
|
+
error_event = {
|
|
558
|
+
"type": "error",
|
|
559
|
+
"timestamp": int(time.time() * 1000),
|
|
560
|
+
"log_id": ctx.logid,
|
|
561
|
+
"run_id": run_id,
|
|
562
|
+
"code": str(err.code),
|
|
563
|
+
"error_msg": str(ex),
|
|
564
|
+
}
|
|
565
|
+
yield sse_event_func(error_event)
|
|
566
|
+
|
|
567
|
+
|
|
453
568
|
def get_stream_runner(is_agent: bool) -> BaseStreamRunner:
|
|
454
569
|
if is_agent:
|
|
455
570
|
return AgentStreamRunner()
|
|
@@ -10,13 +10,13 @@ coze_coding_utils/file/file.py,sha256=fBda18EGSQZ3Xl8OqEaGAb5Rd90_SmhJ1k0jgQk2v7
|
|
|
10
10
|
coze_coding_utils/helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
coze_coding_utils/helper/agent_helper.py,sha256=q1ZM30xLXoW-m0NJmJ_Y0M-kUAQCBstG_j7xkqsyRSU,22546
|
|
12
12
|
coze_coding_utils/helper/graph_helper.py,sha256=UNtqqiQNAQ4319qcC1vHiLYIL2eGzvGQRgXu3mgLq8Y,8893
|
|
13
|
-
coze_coding_utils/helper/stream_runner.py,sha256=
|
|
13
|
+
coze_coding_utils/helper/stream_runner.py,sha256=K446dPUdwemA0_FwbtyscQz5hGmhRxUCG0XYUTFVSI8,24329
|
|
14
14
|
coze_coding_utils/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
coze_coding_utils/log/common.py,sha256=mUNkCm68oaPaI6-a5UwLf87AfhrMnVPkEuri16guqKc,168
|
|
16
16
|
coze_coding_utils/log/config.py,sha256=Qkw3JRuGUKJ6CBY7WqHJOFeyCU47cArvUtMsSBifFMo,195
|
|
17
17
|
coze_coding_utils/log/err_trace.py,sha256=iwt5g8-AX0N2KuUpuLXjk5PocL6NZdAGddJh27Sxp_o,2954
|
|
18
18
|
coze_coding_utils/log/loop_trace.py,sha256=68sI1AHKd8oeLti-7trBygTZqIiUu_TcHt93DLItkXQ,2129
|
|
19
|
-
coze_coding_utils/log/node_log.py,sha256=
|
|
19
|
+
coze_coding_utils/log/node_log.py,sha256=cqx0l8hgDj8adydj5lYQbpnFwyd3k_QyNeq43Qsvl1I,18011
|
|
20
20
|
coze_coding_utils/log/parser.py,sha256=XuzOkO8SzTbBP_XL1RMFzjqG4IG0iSRStAA2M6latt8,9728
|
|
21
21
|
coze_coding_utils/log/write_log.py,sha256=lDu4_Tjk6eYtWBXI-OOYLqe5ejqTtGnDcrH6M7fMQ2A,7223
|
|
22
22
|
coze_coding_utils/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -32,7 +32,7 @@ coze_coding_utils/openai/types/request.py,sha256=IuNMT2Ce1--_32R30Q2q7Lb2dAwKNy3
|
|
|
32
32
|
coze_coding_utils/openai/types/response.py,sha256=pjHHVR8LSMVFCc3fGzKqXrdoKDIfSCJEfICd_X9Nohc,4808
|
|
33
33
|
coze_coding_utils/runtime_ctx/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
|
|
34
34
|
coze_coding_utils/runtime_ctx/context.py,sha256=G8ld-WnQ1pTJe5OOXC_dTbagXj9IxmpRiPM4X_jWW6o,3992
|
|
35
|
-
coze_coding_utils-0.2.
|
|
36
|
-
coze_coding_utils-0.2.
|
|
37
|
-
coze_coding_utils-0.2.
|
|
38
|
-
coze_coding_utils-0.2.
|
|
35
|
+
coze_coding_utils-0.2.4.dist-info/METADATA,sha256=clx-L3s4LykCyhZxHNxKCc_Ip_u1AY3cpdyxqQkVL74,977
|
|
36
|
+
coze_coding_utils-0.2.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
37
|
+
coze_coding_utils-0.2.4.dist-info/licenses/LICENSE,sha256=lzckZhAjHlpSJcWvppoST095IHFpBwKiB2pKcBv7vP4,1078
|
|
38
|
+
coze_coding_utils-0.2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|