Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ CODEBUDDY2API_MAX_CONCURRENT=64
# Per-account in-flight limit; zero keeps account capacity unlimited.
# 单账号在途请求上限;设为 0 保持账号容量不受限。
# CODEBUDDY2API_MAX_INFLIGHT_PER_ACCOUNT=0
# Optional system prompt prefix; empty adds no gateway default, prepend uses the routed profile prompt.
# 可选系统提示词前缀;empty 不附加网关默认提示词,prepend 按后端插入精简官方提示词。
# CODEBUDDY2API_PROMPT_MODE=empty
# Optional session/attempt tracing; legacy keeps existing upstream header behavior.
# 可选的会话/尝试追踪;legacy 保持现有上游请求头行为。
# CODEBUDDY2API_REQUEST_CONTEXT_MODE=legacy
Expand Down
27 changes: 20 additions & 7 deletions app/client_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

from .site_routing import domain_for_auth, profile_for_auth, profile_product, profile_region

CLI_VERSION = "2.149.0"
WORKBUDDY_VERSION = "5.5.2"
WORKBUDDY_CLI_VERSION = "2.137.1"
CLI_VERSION = "2.165.0"
WORKBUDDY_VERSION = "5.9.2"
WORKBUDDY_CLI_VERSION = CLI_VERSION
INTL_CLI_VERSION = "2.161.0"
INTL_WORKBUDDY_VERSION = "5.8.4"
INTL_WORKBUDDY_CLI_VERSION = INTL_CLI_VERSION
CLI_USER_AGENT = f"CLI/{CLI_VERSION} CodeBuddy/{CLI_VERSION}"

SDK_HEADERS = {
Expand All @@ -26,14 +29,23 @@
}


def _versions(profile):
if profile_region(profile) == "intl":
return INTL_CLI_VERSION, INTL_WORKBUDDY_VERSION, INTL_WORKBUDDY_CLI_VERSION
return CLI_VERSION, WORKBUDDY_VERSION, WORKBUDDY_CLI_VERSION


def identity_headers(profile: str) -> dict:
"""Use verified package versions without reading desktop settings or device identities."""
cli_version, workbuddy_version, workbuddy_cli_version = _versions(profile)
if profile_product(profile) == "cli":
return {"User-Agent": CLI_USER_AGENT, "X-IDE-Type": "CLI", "X-IDE-Name": "CLI", "X-IDE-Version": CLI_VERSION}
return {"User-Agent": f"CLI/{cli_version} CodeBuddy/{cli_version}",
"X-IDE-Type": "CLI", "X-IDE-Name": "CLI", "X-IDE-Version": cli_version}
name = "WorkBuddy AI" if profile_region(profile) == "intl" else "WorkBuddy"
return {
"User-Agent": f"WorkBuddy/{WORKBUDDY_VERSION} {name}/{WORKBUDDY_VERSION} CLI/{WORKBUDDY_CLI_VERSION}",
"X-IDE-Type": "WorkBuddy", "X-IDE-Name": "WorkBuddy", "X-IDE-Version": WORKBUDDY_VERSION,
"User-Agent": f"WorkBuddy/{workbuddy_version} {name}/{workbuddy_version} CLI/{workbuddy_cli_version}",
"X-IDE-Type": "WorkBuddy", "X-IDE-Name": "WorkBuddy", "X-IDE-Version": workbuddy_version,
"X-Private-Data": "true",
}


Expand Down Expand Up @@ -168,6 +180,7 @@ def canonical_uid(account) -> str:


def catalog_cache_key(profile: str, identity: str | None = None) -> str:
revision = CLI_VERSION if profile_product(profile) == "cli" else f"{WORKBUDDY_VERSION}:{WORKBUDDY_CLI_VERSION}"
cli_version, workbuddy_version, workbuddy_cli_version = _versions(profile)
revision = cli_version if profile_product(profile) == "cli" else f"{workbuddy_version}:{workbuddy_cli_version}"
key = f"{profile}:{revision}"
return f"{key}:account:{identity}" if identity is not None else key
89 changes: 86 additions & 3 deletions app/desensitize.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,16 @@
)

# Limit substitutions to known templates without globally stripping whitespace.
def _template_pattern(text: str) -> re.Pattern:
def _template_source(text: str) -> str:
words = [(_ZWSP + "*").join(re.escape(char) for char in word) for word in text.split(" ")]
return re.compile(r"(?<!\w)" + r"[\s\u200b]+".join(words) + _ZWSP + r"*(?!\w)", re.IGNORECASE)
return r"[\s\u200b]+".join(words) + _ZWSP + r"*"


def _template_pattern(text: str) -> re.Pattern:
return re.compile(r"(?<!\w)" + _template_source(text) + r"(?!\w)", re.IGNORECASE)


_DSH_WORKBUDDY_IDENTITY = "You are WorkBuddy, a powerful AI assistant"
_WORKBUDDY_IDENTITY = "You are CodeBuddy, Tencent's official CLI"
_CLAUDE_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude"
_TEMPLATE_REPLACEMENTS = tuple(
Expand All @@ -123,10 +128,32 @@ def _template_pattern(text: str) -> re.Pattern:
("You are a Claude agent, built on Anthropic's Claude Agent SDK", _WORKBUDDY_IDENTITY),
("Main branch (you will usually use this for PRs)",
"Main branch (you will usually use this for PR)"),
("You are an AI agent powered by DeepSeek Harness.", "You are a helpful AI assistant."),
("You are an AI agent powered by DeepSeek Harness.", _DSH_WORKBUDDY_IDENTITY + "."),
)
)
_WORKBUDDY_IDENTITY_PATTERN = _template_pattern(_WORKBUDDY_IDENTITY)
_CHANNEL_IDENTITY_PATTERN = _template_pattern("You are an AI agent powered by DeepSeek Harness")
_DSH_CODING_PATTERN = re.compile(
r"(?<!\w)" + _template_source("You are a coding agent powered by the")
+ r"[\s\u200b]+(?P<model>[^\r\n]{1,200}?)[\s\u200b]+"
+ _template_source("model") + r"(?!\w)",
re.IGNORECASE,
)
_DSH_MINIMAL_PATTERN = _template_pattern("You are a helpful software engineer assistant")
_DSH_DIRECT_PATTERN = _template_pattern("You are DeepSeek Harness")
_DSH_WEB_GUI_PATTERN = _template_pattern("DeepSeek Harness Web GUI")
_CHANNEL_IDENTITY_FIXED = _DSH_WORKBUDDY_IDENTITY

WORKBUDDY_SYSTEM_PROMPT = (
"You are WorkBuddy, a powerful AI assistant.\n\n"
"Your main goal is to follow the USER's instructions at each message, denoted by the <user_query> tag."
)
CODEBUDDY_SYSTEM_PROMPT = (
"You are CodeBuddy Code.\n\n"
"You are an interactive CLI tool that helps users with software engineering tasks. "
"Use the instructions below and the tools available to you to assist the user. "
"Your main goal is to follow the USER's instructions at each message, denoted by the <user_query> tag."
)
_GIT_STATUS_CONTEXT = re.compile(
r"^\s*(?:gitStatus:\s*|#\s*gitStatus\s*\n)"
r"This is the git status at the start of the conversation\.", re.IGNORECASE | re.MULTILINE,
Expand Down Expand Up @@ -226,6 +253,22 @@ def desensitize_text(text: str) -> str:
return _PATTERN.sub(lambda m: _zero_width_split(m.group(0)), text)


def _replace_dsh_coding_persona(match: re.Match) -> str:
model = match.group("model").strip()
return f"{_DSH_WORKBUDDY_IDENTITY}. This conversation is powered by the {model} model"


def desensitize_channel_text(text: str) -> str:
"""Convert official DSH role prompts without changing unrelated brand references."""
if not text:
return text
text = _DSH_CODING_PATTERN.sub(_replace_dsh_coding_persona, text)
text = _CHANNEL_IDENTITY_PATTERN.sub(_CHANNEL_IDENTITY_FIXED, text)
text = _DSH_MINIMAL_PATTERN.sub(_CHANNEL_IDENTITY_FIXED, text)
text = _DSH_DIRECT_PATTERN.sub(_CHANNEL_IDENTITY_FIXED, text)
return _DSH_WEB_GUI_PATTERN.sub("WorkBuddy Web GUI", text)


def _iter_text_blocks(content):
"""Yield text containers and keys from string or block-based Chat content."""
if isinstance(content, str):
Expand Down Expand Up @@ -400,6 +443,46 @@ def desensitize_body(body: dict, roles: tuple[str, ...] = ("system",),
return nb if changed else body


def desensitize_channel_body(body: dict) -> dict:
"""Remove the known channel identity from instruction and assistant text."""
if not body.get("messages"):
return body
messages = []
for message in body["messages"]:
if not isinstance(message, dict) or message.get("role") not in ("system", "developer", "assistant"):
messages.append(message)
continue
adapted = dict(message)
content = message.get("content")
if isinstance(content, str):
adapted["content"] = desensitize_channel_text(content)
elif isinstance(content, list):
adapted["content"] = [
{**block, "text": desensitize_channel_text(block["text"])}
if isinstance(block, dict) and block.get("type") == "text" and isinstance(block.get("text"), str)
else block
for block in content
]
messages.append(adapted)
return {**body, "messages": messages}


def apply_system_prompt_mode(body: dict, profile: str, mode: str) -> dict:
"""Apply an optional profile prompt without mutating canonical messages."""
if mode == "empty":
return body
if mode != "prepend":
raise ValueError("prompt_mode 仅支持 empty 或 prepend")
prompt = WORKBUDDY_SYSTEM_PROMPT if profile in ("cn-work", "intl-work") else CODEBUDDY_SYSTEM_PROMPT
messages = body.get("messages")
if not isinstance(messages, list):
return body
if messages and isinstance(messages[0], dict) and messages[0].get("role") == "system" \
and _content_to_text(messages[0].get("content")) == prompt:
return body
return {**body, "messages": [{"role": "system", "content": prompt}, *messages]}


# ---------------------------------------------------------------------------
# Standalone template adaptation checks
# ---------------------------------------------------------------------------
Expand Down
15 changes: 10 additions & 5 deletions app/growth_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,19 @@ def _growth_request(client, token, uid, domain, method, path, *, body=None, plat
if len(raw) + len(chunk) > MAX_RESPONSE_BYTES:
raise Failure("protocol", response.status_code)
raw.extend(chunk)
if response.status_code != 200:
raise Failure("http", response.status_code)
try:
payload = json.loads(raw)
except (ValueError, RecursionError):
if response.status_code != 200:
raise Failure("http", response.status_code) from None
raise Failure("protocol", response.status_code) from None
code = payload.get("code") if isinstance(payload, dict) else None
message = str(payload.get("msg") or "")[:200] if isinstance(payload, dict) else ""
if response.status_code != 200:
raise Failure("http", response.status_code, code if type(code) is int else None, message)
if code != 0:
raise Failure("business", response.status_code,
code if type(code) is int else None,
str((payload or {}).get("msg") or "")[:200])
code if type(code) is int else None, message)
data = payload.get("data")
return data if data is not None else {}

Expand Down Expand Up @@ -184,7 +186,10 @@ def sweep_account(token, uid="", domain=""):
claimed.append(code)
credit += got_credit
energy += got_energy
except Failure:
except Failure as error:
errors += 1
if error.details.get("code") == 11140:
return {"ok": False, "error": error.details, "accepted": accepted, "claimed": claimed,
"credit": credit, "energy": energy, "errors": errors}
return {"ok": errors == 0, "accepted": accepted, "claimed": claimed, "credit": credit,
"energy": energy, "errors": errors}
39 changes: 31 additions & 8 deletions app/request_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,32 @@ def _content_parts(content):
@dataclass(frozen=True)
class Attempt:
id: str
request_id: str
trace_id: str
index: int
span: str


_WORKBUDDY_PROFILES = frozenset({"cn-work", "intl-work"})


def _uuid_text(value):
return str(uuid.UUID(hex=value)) if isinstance(value, str) and len(value) == 32 else str(value)


def build_attempt_headers(headers, profile, *, root, message_id, request_id, trace_id, span, parent):
"""Build one upstream attempt with profile-specific ID relationships."""
if profile in _WORKBUDDY_PROFILES:
message_id, request_id = _uuid_text(message_id), _uuid_text(request_id)
else:
request_id, trace_id = message_id, root
return {**headers, "X-Request-ID": request_id, "X-Conversation-Message-ID": message_id,
"X-Conversation-Request-ID": root, "X-Root-Request-ID": root, "X-Trace-ID": trace_id,
"traceparent": f"00-{trace_id}-{span}-01", "b3": f"{trace_id}-{span}-1-{parent}",
"X-B3-TraceId": trace_id, "X-B3-SpanId": span, "X-B3-ParentSpanId": parent,
"X-B3-Sampled": "1"}


class RequestContext:
def __init__(self, protocol, mode="legacy", headers=(), *, capability_guard=True):
self.request_id = uuid.uuid4().hex
Expand Down Expand Up @@ -118,18 +140,19 @@ def conversation_id(self, profile, account):
def start_attempt(self):
with self._lock:
self._attempt_index += 1
self.attempt = Attempt(uuid.uuid4().hex, self._attempt_index, secrets.token_hex(8))
self.attempt = Attempt(id=uuid.uuid4().hex, request_id=uuid.uuid4().hex,
trace_id=uuid.uuid4().hex, index=self._attempt_index,
span=secrets.token_hex(8))
return self.attempt

def attempt_headers(self, headers, attempt):
def attempt_headers(self, headers, attempt, profile=None):
if not self.scoped:
return dict(headers)
root, span = self.request_id, attempt.span
return {**headers, "X-Request-ID": attempt.id, "X-Conversation-Message-ID": attempt.id,
"X-Conversation-Request-ID": root, "X-Root-Request-ID": root, "X-Trace-ID": root,
"traceparent": f"00-{root}-{span}-01", "b3": f"{root}-{span}-1-{self._root_span}",
"X-B3-TraceId": root, "X-B3-SpanId": span, "X-B3-ParentSpanId": self._root_span,
"X-B3-Sampled": "1"}
return build_attempt_headers(
headers, profile, root=self.request_id, message_id=attempt.id,
request_id=attempt.request_id, trace_id=attempt.trace_id,
span=attempt.span, parent=self._root_span,
)

def attempt_metadata(self):
data = {"request_id": self.request_id, "session_source": self.session_source}
Expand Down
2 changes: 2 additions & 0 deletions app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ def _item(default, type_, label, *, mode="hot", env=None, minimum=None, maximum=
"admin_allowed_origins": _item("", "string", "管理页额外信任来源", env="CODEBUDDY2API_ADMIN_ORIGINS",
allow_empty=True, max_length=2000, validator=normalize_allowed_origins),
"desensitize": _item(False, "boolean", "提示词脱敏"),
"prompt_mode": _item("empty", "string", "系统提示词模式",
env="CODEBUDDY2API_PROMPT_MODE", choices=["empty", "prepend"]),
"no_compact": _item(False, "boolean", "保留提示词全文"),
"keep_tool_metadata": _item(False, "boolean", "保留工具描述", env="CODEBUDDY2API_KEEP_TOOL_METADATA"),
"skip_check": _item(False, "boolean", "跳过启动预检", mode="restart"),
Expand Down
Loading
Loading