coze-coding-utils 0.2.7a1__tar.gz → 0.2.8a2__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.7a1 → coze_coding_utils-0.2.8a2}/PKG-INFO +1 -1
  2. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/pyproject.toml +1 -1
  3. coze_coding_utils-0.2.8a2/src/coze_coding_utils/log/config.py +28 -0
  4. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/log/loop_trace.py +0 -2
  5. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/log/node_log.py +36 -7
  6. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/log/write_log.py +28 -18
  7. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/runtime_ctx/context.py +18 -0
  8. coze_coding_utils-0.2.7a1/src/coze_coding_utils/log/config.py +0 -10
  9. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/.gitignore +0 -0
  10. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/LICENSE +0 -0
  11. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/README.md +0 -0
  12. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/__init__.py +0 -0
  13. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/error/__init__.py +0 -0
  14. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/error/classifier.py +0 -0
  15. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/error/codes.py +0 -0
  16. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/error/exceptions.py +0 -0
  17. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/error/patterns.py +0 -0
  18. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/file/__init__.py +0 -0
  19. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/file/file.py +0 -0
  20. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/helper/__init__.py +0 -0
  21. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/helper/agent_helper.py +0 -0
  22. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/helper/graph_helper.py +0 -0
  23. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/helper/stream_runner.py +0 -0
  24. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/log/__init__.py +0 -0
  25. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/log/common.py +0 -0
  26. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/log/err_trace.py +0 -0
  27. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/log/parser.py +0 -0
  28. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/messages/__init__.py +0 -0
  29. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/messages/client.py +0 -0
  30. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/messages/server.py +0 -0
  31. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/__init__.py +0 -0
  32. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/converter/__init__.py +0 -0
  33. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/converter/request_converter.py +0 -0
  34. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/converter/response_converter.py +0 -0
  35. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/handler.py +0 -0
  36. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/types/__init__.py +0 -0
  37. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/types/request.py +0 -0
  38. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/openai/types/response.py +0 -0
  39. {coze_coding_utils-0.2.7a1 → coze_coding_utils-0.2.8a2}/src/coze_coding_utils/runtime_ctx/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coze-coding-utils
3
- Version: 0.2.7a1
3
+ Version: 0.2.8a2
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.7a1"
7
+ version = "0.2.8a2"
8
8
  description = "Utilities for Coze coding client runtime context and helpers."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1,28 @@
1
+ """
2
+ Application configuration
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+
7
+ from coze_coding_utils.runtime_ctx.context import ENV_WORKTREE_NAME
8
+
9
+ # Logging
10
+ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
11
+
12
+ LOG_DIR = Path(os.getenv("COZE_LOG_DIR", "/tmp/app/work/logs/bypass"))
13
+
14
+
15
+ def resolve_log_dir() -> Path:
16
+ """Resolve the per-process log directory based on COZE_WORKTREE_NAME.
17
+
18
+ 沙箱拉起产物子进程时通过环境变量 COZE_WORKTREE_NAME 注入当前 worktree 名(主仓为空)。
19
+ LogWatcherManager 在 services/devbox/app/services/log_watcher.py 里按 worktree 隔离监听目录:
20
+ - 主仓 → LOG_DIR/
21
+ - 具名 worktree → LOG_DIR/worktrees/<name>/
22
+ SDK 这里用同样规则解析写入目录,watcher 才能拾起对应日志,按正确 worktree group 推 IDE。
23
+ """
24
+ worktree_name = os.getenv(ENV_WORKTREE_NAME, "")
25
+ if worktree_name:
26
+ return LOG_DIR / "worktrees" / worktree_name
27
+ return LOG_DIR
28
+
@@ -33,7 +33,6 @@ def init_run_config(graph, ctx):
33
33
  "commit_hash": commit_hash,
34
34
  "coze_entity_type": ctx.rpc_persist_res_rec_root_entity_type,
35
35
  "coze_entity_id": ctx.rpc_persist_res_rec_root_entity_id,
36
- "coze_tt_env": ctx.x_tt_env,
37
36
  "coze_run_id" : ctx.run_id,
38
37
  }
39
38
  )
@@ -58,7 +57,6 @@ def init_agent_config(graph, ctx):
58
57
  "commit_hash": commit_hash,
59
58
  "coze_entity_type": ctx.rpc_persist_res_rec_root_entity_type,
60
59
  "coze_entity_id": ctx.rpc_persist_res_rec_root_entity_id,
61
- "coze_tt_env": ctx.x_tt_env,
62
60
  "coze_run_id" : ctx.run_id,
63
61
  }
64
62
  )
@@ -1,7 +1,7 @@
1
1
  import time
2
2
  import logging
3
3
  from uuid import UUID
4
- from coze_coding_utils.log.config import LOG_DIR
4
+ from coze_coding_utils.log.config import LOG_DIR, resolve_log_dir
5
5
  from coze_coding_utils.log.common import get_execute_mode, is_prod
6
6
  import uuid
7
7
  from langchain_core.callbacks import BaseCallbackHandler
@@ -26,8 +26,11 @@ class ParamInfo:
26
26
 
27
27
  # 2. 确保日志目录存在
28
28
  # 尝试使用可写目录,先尝试/app目录,如果失败则使用/tmp目录
29
+
29
30
  try:
30
- LOG_FILE = os.path.join(LOG_DIR, 'app.log')
31
+ _resolved_log_dir = resolve_log_dir()
32
+ os.makedirs(_resolved_log_dir, exist_ok=True)
33
+ LOG_FILE = os.path.join(_resolved_log_dir, 'app.log')
31
34
  # 测试写入权限
32
35
  with open(LOG_FILE, 'a') as f:
33
36
  pass
@@ -99,7 +102,7 @@ def create_log_entry(level="info", message="", timestamp=None, log_id=None, late
99
102
  input_data="", output_data="", node_id="", project_id="", commit_id="",
100
103
  execute_mode="run", caller="", node_type="", node_title="",
101
104
  token="", cost="", error_code="", error_message="", event_type="", execution_id="", node_name="",
102
- method=""):
105
+ method="", worktree_name="", user_id=""):
103
106
  """
104
107
  创建符合要求的日志条目
105
108
  :param level: 日志级别
@@ -124,6 +127,8 @@ def create_log_entry(level="info", message="", timestamp=None, log_id=None, late
124
127
  :param execution_id: 执行唯一ID(可选)
125
128
  :param node_name: 节点名称
126
129
  :param method: 方法名称
130
+ :param worktree_name: 当前 worktree 名(主仓为空字符串),便于多 worktree 并发排查
131
+ :param user_id: 当前用户 ID(沙箱 owner / 调用方),便于多用户并发排查
127
132
  :return: 格式化的日志字典
128
133
  """
129
134
  if timestamp is None:
@@ -156,10 +161,12 @@ def create_log_entry(level="info", message="", timestamp=None, log_id=None, late
156
161
  "execute_id": execution_id,
157
162
  "node_name": node_name,
158
163
  "method": method,
164
+ "worktree_name": worktree_name,
165
+ "user_id": user_id,
159
166
  }
160
167
 
161
168
 
162
- def log_workflow_start(project_id, commit_id, log_id=None, execute_id="", input_data="", method=""):
169
+ def log_workflow_start(project_id, commit_id, log_id=None, execute_id="", input_data="", method="", worktree_name="", user_id=""):
163
170
  """
164
171
  记录流程开始日志
165
172
  :param project_id: 项目ID
@@ -167,6 +174,8 @@ def log_workflow_start(project_id, commit_id, log_id=None, execute_id="", input_
167
174
  :param is_test_run: 是否试运行
168
175
  :param log_id: 日志ID(可选)
169
176
  :param execute_id: 执行唯一ID(可选)
177
+ :param worktree_name: 当前 worktree 名
178
+ :param user_id: 当前用户 ID
170
179
  """
171
180
  event_type = "test_run_start" if not is_prod() else "run_start"
172
181
  execute_mode = "test_run" if not is_prod() else "run"
@@ -184,13 +193,15 @@ def log_workflow_start(project_id, commit_id, log_id=None, execute_id="", input_
184
193
  execution_id=execute_id,
185
194
  input_data=input_data,
186
195
  method=method,
196
+ worktree_name=worktree_name,
197
+ user_id=user_id,
187
198
  )
188
199
 
189
200
  write_log(log_entry)
190
201
 
191
202
 
192
203
  def log_workflow_end(execution_id, output=None, total_time=None, status="success", token_consumed=None,
193
- error_reason=None, error_code=None, is_test_run=False, log_id="", method=""):
204
+ error_reason=None, error_code=None, is_test_run=False, log_id="", method="", worktree_name="", user_id=""):
194
205
  """
195
206
  记录流程结束日志
196
207
  :param execution_id: 执行唯一ID
@@ -201,6 +212,8 @@ def log_workflow_end(execution_id, output=None, total_time=None, status="success
201
212
  :param error_reason: 错误原因
202
213
  :param error_code: 错误码
203
214
  :param is_test_run: 是否试运行
215
+ :param worktree_name: 当前 worktree 名
216
+ :param user_id: 当前用户 ID
204
217
  """
205
218
  level = "error" if status == "error" else "info"
206
219
  execute_mode = "test_run" if is_test_run else "run"
@@ -222,6 +235,8 @@ def log_workflow_end(execution_id, output=None, total_time=None, status="success
222
235
  execution_id=execution_id,
223
236
  log_id=log_id,
224
237
  method=method,
238
+ worktree_name=worktree_name,
239
+ user_id=user_id,
225
240
  )
226
241
 
227
242
  write_log(log_entry)
@@ -271,7 +286,9 @@ class Logger(BaseCallbackHandler):
271
286
  event_type="node_start",
272
287
  log_id=self.runtime_ctx.logid,
273
288
  method=self.runtime_ctx.method,
274
- node_type="condition"
289
+ node_type="condition",
290
+ worktree_name=self.runtime_ctx.worktree_name,
291
+ user_id=self.runtime_ctx.user_id,
275
292
  )
276
293
  write_log(log_entry)
277
294
  return
@@ -290,6 +307,8 @@ class Logger(BaseCallbackHandler):
290
307
  log_id=self.runtime_ctx.logid,
291
308
  node_name=node_info.name,
292
309
  method=self.runtime_ctx.method,
310
+ worktree_name=self.runtime_ctx.worktree_name,
311
+ user_id=self.runtime_ctx.user_id,
293
312
  )
294
313
  write_log(log_entry)
295
314
 
@@ -321,7 +340,9 @@ class Logger(BaseCallbackHandler):
321
340
  event_type="node_end",
322
341
  log_id=self.runtime_ctx.logid,
323
342
  method=self.runtime_ctx.method,
324
- node_type="condition"
343
+ node_type="condition",
344
+ worktree_name=self.runtime_ctx.worktree_name,
345
+ user_id=self.runtime_ctx.user_id,
325
346
  )
326
347
  write_log(log_entry)
327
348
  return
@@ -340,6 +361,8 @@ class Logger(BaseCallbackHandler):
340
361
  log_id=self.runtime_ctx.logid,
341
362
  node_name=node_info.name,
342
363
  method=self.runtime_ctx.method,
364
+ worktree_name=self.runtime_ctx.worktree_name,
365
+ user_id=self.runtime_ctx.user_id,
343
366
  )
344
367
  write_log(log_entry)
345
368
 
@@ -354,6 +377,8 @@ class Logger(BaseCallbackHandler):
354
377
  execute_id=self.runtime_ctx.run_id,
355
378
  input_data=_serialize_data(inputs),
356
379
  method=self.runtime_ctx.method,
380
+ worktree_name=self.runtime_ctx.worktree_name,
381
+ user_id=self.runtime_ctx.user_id,
357
382
  )
358
383
 
359
384
  def _on_graph_end(self, outputs: Dict[str, Any]):
@@ -367,6 +392,8 @@ class Logger(BaseCallbackHandler):
367
392
  log_id=self.runtime_ctx.logid,
368
393
  is_test_run=not is_prod(),
369
394
  method=self.runtime_ctx.method,
395
+ worktree_name=self.runtime_ctx.worktree_name,
396
+ user_id=self.runtime_ctx.user_id,
370
397
  )
371
398
 
372
399
  def on_chain_error(
@@ -409,6 +436,8 @@ class Logger(BaseCallbackHandler):
409
436
  error_message=str(error),
410
437
  node_name=node_name,
411
438
  method=self.runtime_ctx.method,
439
+ worktree_name=self.runtime_ctx.worktree_name,
440
+ user_id=self.runtime_ctx.user_id,
412
441
  )
413
442
  write_log(error_log_entry)
414
443
 
@@ -5,16 +5,16 @@ from contextvars import ContextVar
5
5
  from typing import Optional
6
6
  from pathlib import Path
7
7
  from coze_coding_utils.runtime_ctx.context import Context
8
- from coze_coding_utils.log.config import LOG_DIR
8
+ from coze_coding_utils.log.config import LOG_DIR, resolve_log_dir
9
9
 
10
10
  request_context: ContextVar[Optional[Context]] = ContextVar('request_context', default=None)
11
11
 
12
12
 
13
13
  class ContextFilter(logging.Filter):
14
-
14
+
15
15
  def filter(self, record: logging.LogRecord) -> bool:
16
16
  ctx = request_context.get()
17
-
17
+
18
18
  if ctx:
19
19
  record.log_id = ctx.logid or ''
20
20
  record.run_id = ctx.run_id or ''
@@ -22,6 +22,8 @@ class ContextFilter(logging.Filter):
22
22
  record.project_id = ctx.project_id or ''
23
23
  record.method = ctx.method or ''
24
24
  record.x_tt_env = ctx.x_tt_env or ''
25
+ record.worktree_name = ctx.worktree_name or ''
26
+ record.user_id = ctx.user_id or ''
25
27
  else:
26
28
  record.log_id = ''
27
29
  record.run_id = ''
@@ -29,7 +31,9 @@ class ContextFilter(logging.Filter):
29
31
  record.project_id = ''
30
32
  record.method = ''
31
33
  record.x_tt_env = ''
32
-
34
+ record.worktree_name = ''
35
+ record.user_id = ''
36
+
33
37
  return True
34
38
 
35
39
 
@@ -44,7 +48,7 @@ class APSchedulerFilter(logging.Filter):
44
48
 
45
49
 
46
50
  class JsonFormatter(logging.Formatter):
47
-
51
+
48
52
  def format(self, record: logging.LogRecord) -> str:
49
53
  log_data = {
50
54
  'message': record.getMessage(),
@@ -57,29 +61,32 @@ class JsonFormatter(logging.Formatter):
57
61
  'project_id': getattr(record, 'project_id', ''),
58
62
  'method': getattr(record, 'method', ''),
59
63
  'x_tt_env': getattr(record, 'x_tt_env', ''),
64
+ 'worktree_name': getattr(record, 'worktree_name', ''),
65
+ 'user_id': getattr(record, 'user_id', ''),
60
66
  'lineno': record.lineno,
61
67
  'funcName': record.funcName,
62
68
  }
63
69
 
64
70
  if record.exc_info:
65
71
  log_data['exc_info'] = self.formatException(record.exc_info)
66
-
72
+
67
73
  for key, value in record.__dict__.items():
68
- if key not in ['name', 'msg', 'args', 'created', 'filename', 'funcName',
69
- 'levelname', 'levelno', 'lineno', 'module', 'msecs',
74
+ if key not in ['name', 'msg', 'args', 'created', 'filename', 'funcName',
75
+ 'levelname', 'levelno', 'lineno', 'module', 'msecs',
70
76
  'message', 'pathname', 'process', 'processName', 'relativeCreated',
71
77
  'thread', 'threadName', 'exc_info', 'exc_text', 'stack_info',
72
78
  'log_id', 'run_id', 'space_id', 'project_id', 'method',
73
- 'x_tt_env', 'rpc_persist_rec_rec_biz_scene',
79
+ 'x_tt_env', 'worktree_name', 'user_id',
80
+ 'rpc_persist_rec_rec_biz_scene',
74
81
  'rpc_persist_coze_record_root_id', 'rpc_persist_rec_root_entity_type',
75
82
  'rpc_persist_rec_root_entity_id']:
76
83
  log_data[key] = value
77
-
84
+
78
85
  return json.dumps(log_data, ensure_ascii=False)
79
86
 
80
87
 
81
88
  class PlainTextFormatter(logging.Formatter):
82
-
89
+
83
90
  def format(self, record: logging.LogRecord) -> str:
84
91
  log_data = {
85
92
  'message': record.getMessage(),
@@ -92,24 +99,27 @@ class PlainTextFormatter(logging.Formatter):
92
99
  'project_id': getattr(record, 'project_id', ''),
93
100
  'method': getattr(record, 'method', ''),
94
101
  'x_tt_env': getattr(record, 'x_tt_env', ''),
102
+ 'worktree_name': getattr(record, 'worktree_name', ''),
103
+ 'user_id': getattr(record, 'user_id', ''),
95
104
  'lineno': record.lineno,
96
105
  'funcName': record.funcName,
97
106
  }
98
-
107
+
99
108
  if record.exc_info:
100
109
  log_data['exc_info'] = self.formatException(record.exc_info)
101
-
110
+
102
111
  for key, value in record.__dict__.items():
103
- if key not in ['name', 'msg', 'args', 'created', 'filename', 'funcName',
104
- 'levelname', 'levelno', 'lineno', 'module', 'msecs',
112
+ if key not in ['name', 'msg', 'args', 'created', 'filename', 'funcName',
113
+ 'levelname', 'levelno', 'lineno', 'module', 'msecs',
105
114
  'message', 'pathname', 'process', 'processName', 'relativeCreated',
106
115
  'thread', 'threadName', 'exc_info', 'exc_text', 'stack_info',
107
116
  'log_id', 'run_id', 'space_id', 'project_id', 'method',
108
- 'x_tt_env', 'rpc_persist_rec_rec_biz_scene',
117
+ 'x_tt_env', 'worktree_name', 'user_id',
118
+ 'rpc_persist_rec_rec_biz_scene',
109
119
  'rpc_persist_coze_record_root_id', 'rpc_persist_rec_root_entity_type',
110
120
  'rpc_persist_rec_root_entity_id']:
111
121
  log_data[key] = value
112
-
122
+
113
123
  return json.dumps(log_data, ensure_ascii=False)
114
124
 
115
125
 
@@ -124,7 +134,7 @@ def setup_logging(
124
134
 
125
135
  if log_file is None:
126
136
  try:
127
- log_dir = Path(LOG_DIR)
137
+ log_dir = Path(resolve_log_dir())
128
138
  log_dir.mkdir(parents=True, exist_ok=True)
129
139
  log_file = str(log_dir / 'app.log')
130
140
  except Exception as e:
@@ -20,10 +20,17 @@ HEADER_RPC_PERSIST_RES_REC_ROOT_ENTITY_ID = (
20
20
  HEADER_RPC_PERSIST_RES_REC_EXT_INFO = (
21
21
  "rpc-persist-res-rec-ext-info" # 扩展信息,json字符串格式
22
22
  )
23
+ # 多 worktree / 多用户排查诊断用:上游可通过 header 透传这两个字段,让本次 ctx 持有它们,
24
+ # 下游的 LangGraph callback / 业务日志直接 record 出来,多会话并发时按字段过滤排查。
25
+ HEADER_X_COZE_WORKTREE_NAME = "x-coze-worktree-name"
26
+ HEADER_X_COZE_USER_ID = "x-coze-user-id"
23
27
 
24
28
  # Environment variable keys
25
29
  ENV_SPACE_ID = "COZE_PROJECT_SPACE_ID"
26
30
  ENV_PROJECT_ID = "COZE_PROJECT_ID"
31
+ # 沙箱拉起产物子进程时注入。worktree_name 主仓为空字符串;user_id 为创建沙箱的用户 ID。
32
+ ENV_WORKTREE_NAME = "COZE_WORKTREE_NAME"
33
+ ENV_USER_ID = "COZE_USER_ID"
27
34
 
28
35
 
29
36
  @dataclass(slots=True)
@@ -36,6 +43,11 @@ class Context:
36
43
  logid: str = ""
37
44
  method: str = ""
38
45
 
46
+ # 多 worktree / 多用户支持:默认读环境变量(沙箱拉起产物时注入),上游 HTTP 请求 header 可覆盖。
47
+ # 用 "" 表示主仓 / 未提供,避免 None 在 logging record 里出现 'None' 字面值。
48
+ worktree_name: str = ""
49
+ user_id: str = ""
50
+
39
51
  x_run_mode: Optional[str] = None
40
52
  x_tt_env: Optional[str] = None
41
53
  x_use_ppe: Optional[str] = None
@@ -58,6 +70,8 @@ def new_context(
58
70
  space_id=os.getenv(ENV_SPACE_ID, ""),
59
71
  project_id=os.getenv(ENV_PROJECT_ID, ""),
60
72
  method=method,
73
+ worktree_name=os.getenv(ENV_WORKTREE_NAME, ""),
74
+ user_id=os.getenv(ENV_USER_ID, ""),
61
75
  )
62
76
  if headers:
63
77
  norm = {k.casefold(): v for k, v in headers.items()}
@@ -67,6 +81,8 @@ def new_context(
67
81
  HEADER_X_USE_PPE: "x_use_ppe",
68
82
  HEADER_X_TT_ENV_FE: "x_tt_env_fe",
69
83
  HEADER_X_RUN_MODE: "x_run_mode",
84
+ HEADER_X_COZE_WORKTREE_NAME: "worktree_name",
85
+ HEADER_X_COZE_USER_ID: "user_id",
70
86
  HEADER_RPC_PERSIST_RES_REC_BIZ_SCENE: "rpc_persist_res_rec_biz_scene",
71
87
  HEADER_RPC_PERSIST_COZE_RECORD_ROOT_ID: "rpc_persist_coze_record_root_id",
72
88
  HEADER_RPC_PERSIST_RES_REC_ROOT_ENTITY_TYPE: "rpc_persist_res_rec_root_entity_type",
@@ -91,6 +107,8 @@ def default_headers(ctx: Context | None) -> Dict[str, str]:
91
107
  "x_use_ppe": HEADER_X_USE_PPE,
92
108
  "x_tt_env_fe": HEADER_X_TT_ENV_FE,
93
109
  "x_run_mode": HEADER_X_RUN_MODE,
110
+ "worktree_name": HEADER_X_COZE_WORKTREE_NAME,
111
+ "user_id": HEADER_X_COZE_USER_ID,
94
112
  "rpc_persist_res_rec_biz_scene": HEADER_RPC_PERSIST_RES_REC_BIZ_SCENE,
95
113
  "rpc_persist_coze_record_root_id": HEADER_RPC_PERSIST_COZE_RECORD_ROOT_ID,
96
114
  "rpc_persist_res_rec_root_entity_type": HEADER_RPC_PERSIST_RES_REC_ROOT_ENTITY_TYPE,
@@ -1,10 +0,0 @@
1
- """
2
- Application configuration
3
- """
4
- import os
5
- from pathlib import Path
6
-
7
- # Logging
8
- LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
9
-
10
- LOG_DIR = Path(os.getenv("COZE_LOG_DIR", "/tmp/app/work/logs/bypass"))