coze-coding-utils 0.2.3a2__tar.gz → 0.2.4__tar.gz

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 (39) hide show
  1. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/PKG-INFO +1 -1
  2. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/pyproject.toml +1 -1
  3. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/helper/stream_runner.py +138 -23
  4. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/node_log.py +0 -1
  5. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/.gitignore +0 -0
  6. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/LICENSE +0 -0
  7. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/README.md +0 -0
  8. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/__init__.py +0 -0
  9. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/error/__init__.py +0 -0
  10. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/error/classifier.py +0 -0
  11. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/error/codes.py +0 -0
  12. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/error/exceptions.py +0 -0
  13. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/error/patterns.py +0 -0
  14. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/error/test_classifier.py +0 -0
  15. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/file/__init__.py +0 -0
  16. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/file/file.py +0 -0
  17. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/helper/__init__.py +0 -0
  18. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/helper/agent_helper.py +0 -0
  19. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/helper/graph_helper.py +0 -0
  20. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/__init__.py +0 -0
  21. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/common.py +0 -0
  22. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/config.py +0 -0
  23. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/err_trace.py +0 -0
  24. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/loop_trace.py +0 -0
  25. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/parser.py +0 -0
  26. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/log/write_log.py +0 -0
  27. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/messages/__init__.py +0 -0
  28. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/messages/client.py +0 -0
  29. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/messages/server.py +0 -0
  30. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/__init__.py +0 -0
  31. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/converter/__init__.py +0 -0
  32. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/converter/request_converter.py +0 -0
  33. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/converter/response_converter.py +0 -0
  34. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/handler.py +0 -0
  35. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/types/__init__.py +0 -0
  36. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/types/request.py +0 -0
  37. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/openai/types/response.py +0 -0
  38. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/runtime_ctx/__init__.py +0 -0
  39. {coze_coding_utils-0.2.3a2 → coze_coding_utils-0.2.4}/src/coze_coding_utils/runtime_ctx/context.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coze-coding-utils
3
- Version: 0.2.3a2
3
+ Version: 0.2.4
4
4
  Summary: Utilities for Coze coding client runtime context and helpers.
5
5
  Project-URL: Homepage, https://code.byted.org/stone/coze-coding-client
6
6
  Author: Bytedance Stone Team
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "coze-coding-utils"
7
- version = "0.2.3a2"
7
+ version = "0.2.4"
8
8
  description = "Utilities for Coze coding client runtime context and helpers."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,20 +4,28 @@ import threading
4
4
  import contextvars
5
5
  import logging
6
6
  from abc import ABC, abstractmethod
7
- from typing import Any, Dict, Iterator, AsyncIterable
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.error import classify_error
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 is_debug:
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
- is_debug = run_config.get("configurable", {}).get("workflow_debug", False)
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 is_debug:
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()
@@ -1,7 +1,6 @@
1
1
  import time
2
2
  import logging
3
3
  from uuid import UUID
4
- from openai import BaseModel
5
4
  from coze_coding_utils.log.config import LOG_DIR
6
5
  from coze_coding_utils.log.common import get_execute_mode, is_prod
7
6
  import uuid