v2.2.0: agent persona selector, Command Code backend, per-provider identity
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## v2.2.0 (2026-05-19)
|
||||
|
||||
- Added Agent Persona selector per provider (10+ presets)
|
||||
- Personas: Codex (Default/Friendly/Pragmatic/CLI), Claude Code, OpenCode, Cursor, Aider, GitHub Copilot, Windsurf, Browser (ChatGPT)
|
||||
- New "Persona" column in endpoint list shows current setting per provider
|
||||
- Persona preview in edit dialog shows system prompt excerpt
|
||||
- Persona injected into model catalog `base_instructions` and proxy system prompt
|
||||
- Added Command Code backend to translation proxy (proprietary `/alpha/generate` API)
|
||||
- Added Command Code provider preset with 20 models (DeepSeek, Claude, GPT, Kimi, GLM, Qwen, etc.)
|
||||
|
||||
## v2.1.1 (2026-05-19)
|
||||
|
||||
- Fixed proxy: map `developer` role to `system` for Chat Completions providers (DeepSeek, Qwen, etc.)
|
||||
|
||||
Binary file not shown.
BIN
codex-launcher_2.2.0_all.deb
Normal file
BIN
codex-launcher_2.2.0_all.deb
Normal file
Binary file not shown.
@@ -24,6 +24,14 @@ model_catalog_json = ""
|
||||
"""
|
||||
|
||||
CHANGELOG = [
|
||||
("2.2.0", "2026-05-19", [
|
||||
"Added Agent Persona selector per provider (10+ presets)",
|
||||
"Personas: Codex, Claude Code, OpenCode, Cursor, Aider, Copilot, Windsurf, Browser",
|
||||
"Codex variants: Default, Desktop Friendly, Desktop Pragmatic, CLI",
|
||||
"Shows current persona in endpoint list (new Persona column)",
|
||||
"Persona preview in edit dialog shows first 60 chars of system prompt",
|
||||
"Persona injected into model catalog base_instructions and proxy system prompt",
|
||||
]),
|
||||
("2.1.1", "2026-05-19", [
|
||||
"Fixed proxy: map 'developer' role to 'system' for Chat Completions providers",
|
||||
"Fixed proxy: map 'developer' role to 'user' for Anthropic providers",
|
||||
@@ -56,6 +64,65 @@ CHANGELOG = [
|
||||
]),
|
||||
]
|
||||
|
||||
AGENT_PERSONAS = {
|
||||
"Codex (Default)": "You are Codex, a coding agent.",
|
||||
"Codex Desktop (GPT-5, Friendly)": (
|
||||
"You are Codex, a coding agent based on GPT-5. You and the user share one workspace, "
|
||||
"and your job is to collaborate with them until their goal is genuinely handled."
|
||||
),
|
||||
"Codex Desktop (GPT-5, Pragmatic)": (
|
||||
"You are Codex, a coding agent based on GPT-5. You and the user share the same workspace "
|
||||
"and collaborate to achieve the user's goals. You are a deeply pragmatic, effective "
|
||||
"software engineer. You take engineering quality seriously."
|
||||
),
|
||||
"Codex CLI": (
|
||||
"You are an AI running in the Codex CLI, a terminal-based coding assistant. "
|
||||
"You are expected to be precise, safe, and helpful. Your default personality and tone "
|
||||
"is concise, direct, and friendly."
|
||||
),
|
||||
"Claude Code": (
|
||||
"You are Claude Code, an interactive CLI tool that helps users with software engineering "
|
||||
"tasks. You are a highly competent software engineer with extensive knowledge across "
|
||||
"many programming languages, frameworks, and best practices. Use concise responses."
|
||||
),
|
||||
"OpenCode": (
|
||||
"You are OpenCode, an interactive CLI tool that helps users with software engineering "
|
||||
"tasks. You are powered by a state-of-the-art AI model. Be concise, direct, and to the "
|
||||
"point. Use GitHub-flavored markdown."
|
||||
),
|
||||
"Cursor": (
|
||||
"You are Cursor, an AI-powered code editor assistant. You help users write, refactor, "
|
||||
"and debug code efficiently. Provide precise, actionable suggestions."
|
||||
),
|
||||
"Aider": (
|
||||
"You are aider, an AI pair programming assistant. You help users edit code in their "
|
||||
"local git repository. Make concise changes. Search files with grep/glob patterns."
|
||||
),
|
||||
"GitHub Copilot": (
|
||||
"You are GitHub Copilot, an AI coding assistant. Help the user write code, debug issues, "
|
||||
"and understand codebases. Be concise and provide accurate code suggestions."
|
||||
),
|
||||
"Windsurf": (
|
||||
"You are Windsurf, an AI-powered IDE assistant. Help with coding tasks including writing, "
|
||||
"refactoring, and debugging. Provide precise, well-structured code suggestions."
|
||||
),
|
||||
"Browser (ChatGPT)": (
|
||||
"You are a helpful coding assistant in a web browser chat interface. "
|
||||
"Help the user with software engineering tasks. Be clear and thorough."
|
||||
),
|
||||
}
|
||||
|
||||
PERSONA_DISPLAY_LEN = 60
|
||||
|
||||
def persona_short_key(endpoint):
|
||||
bi = endpoint.get("base_instructions", "") or ""
|
||||
for key, val in AGENT_PERSONAS.items():
|
||||
if val == bi:
|
||||
return key
|
||||
if bi:
|
||||
return f"Custom: {bi[:40]}..."
|
||||
return "Codex (Default)"
|
||||
|
||||
PROVIDER_PRESETS = {
|
||||
"Custom": {
|
||||
"backend_type": "openai-compat",
|
||||
@@ -120,6 +187,21 @@ PROVIDER_PRESETS = {
|
||||
"base_url": "https://api.kilo.ai/api/gateway",
|
||||
"models": [],
|
||||
},
|
||||
"Command Code": {
|
||||
"backend_type": "command-code",
|
||||
"base_url": "https://api.commandcode.ai",
|
||||
"models": [
|
||||
"deepseek/deepseek-v4-flash", "deepseek/deepseek-v4-pro",
|
||||
"anthropic:claude-sonnet-4-6", "anthropic:claude-haiku-4-5-20251001",
|
||||
"anthropic:claude-opus-4-7", "anthropic:claude-opus-4-6",
|
||||
"openai:gpt-5.5", "openai:gpt-5.4", "openai:gpt-5.4-mini", "openai:gpt-5.3-codex",
|
||||
"moonshotai/Kimi-K2.6", "moonshotai/Kimi-K2.5",
|
||||
"zai-org/GLM-5.1", "zai-org/GLM-5",
|
||||
"MiniMaxAI/MiniMax-M2.7", "MiniMaxAI/MiniMax-M2.5",
|
||||
"Qwen/Qwen3.6-Max-Preview", "Qwen/Qwen3.6-Plus",
|
||||
"stepfun/Step-3.5-Flash", "google/gemini-3.1-flash-lite",
|
||||
],
|
||||
},
|
||||
"OpenRouter": {
|
||||
"backend_type": "openai-compat",
|
||||
"base_url": "https://openrouter.ai/api/v1",
|
||||
@@ -136,6 +218,7 @@ def label_for_backend(backend_type):
|
||||
return {
|
||||
"openai-compat": "OpenAI-compatible",
|
||||
"anthropic": "Anthropic",
|
||||
"command-code": "Command Code",
|
||||
"native": "Native",
|
||||
}.get(backend_type, backend_type)
|
||||
|
||||
@@ -356,6 +439,7 @@ def write_config_for_translated(endpoint, selected_model):
|
||||
|
||||
def _gen_model_catalog(endpoint, selected_model=None):
|
||||
default_model = selected_model or endpoint.get("default_model")
|
||||
base_instr = endpoint.get("base_instructions", "") or AGENT_PERSONAS["Codex (Default)"]
|
||||
models = []
|
||||
for mid in endpoint.get("models", []):
|
||||
models.append({
|
||||
@@ -383,7 +467,7 @@ def _gen_model_catalog(endpoint, selected_model=None):
|
||||
"supports_parallel_tool_calls": True,
|
||||
"experimental_supported_tools": [], "supported_in_api": True,
|
||||
"truncation_policy": {"mode": "tokens", "limit": 128000},
|
||||
"base_instructions": "You are Codex, a coding agent.",
|
||||
"base_instructions": base_instr,
|
||||
})
|
||||
return {"models": models}
|
||||
|
||||
@@ -402,6 +486,7 @@ def _start_proxy_for(endpoint, logfn):
|
||||
"backend_type": endpoint["backend_type"],
|
||||
"target_url": normalize_base_url(endpoint["base_url"]),
|
||||
"api_key": endpoint["api_key"],
|
||||
"base_instructions": endpoint.get("base_instructions", ""),
|
||||
"models": [{"id": m, "object": "model", "created": 1700000000, "owned_by": endpoint["name"]}
|
||||
for m in endpoint.get("models", [])],
|
||||
}
|
||||
@@ -500,7 +585,7 @@ class LauncherWin(Gtk.Window):
|
||||
# header row
|
||||
hdr = Gtk.Box(spacing=8)
|
||||
vbox.pack_start(hdr, False, False, 0)
|
||||
lbl = Gtk.Label(label="<b>Codex Launcher v2.1.1</b>")
|
||||
lbl = Gtk.Label(label="<b>Codex Launcher v2.2.0</b>")
|
||||
lbl.set_use_markup(True)
|
||||
hdr.pack_start(lbl, False, False, 0)
|
||||
changelog_btn = Gtk.Button(label="Changelog")
|
||||
@@ -1240,9 +1325,9 @@ class EndpointMgr(Gtk.Window):
|
||||
|
||||
sw = Gtk.ScrolledWindow()
|
||||
vbox.pack_start(sw, True, True, 0)
|
||||
self._store = Gtk.ListStore(str, str, str, str) # name, provider, backend, default_model
|
||||
self._store = Gtk.ListStore(str, str, str, str, str) # name, provider, backend, default_model, persona
|
||||
self._tree = Gtk.TreeView(model=self._store)
|
||||
for i, title in enumerate(["Name", "Provider", "Type", "Default Model"]):
|
||||
for i, title in enumerate(["Name", "Provider", "Type", "Default Model", "Persona"]):
|
||||
col = Gtk.TreeViewColumn(title, Gtk.CellRendererText(), text=i)
|
||||
col.set_resizable(True)
|
||||
self._tree.append_column(col)
|
||||
@@ -1275,7 +1360,8 @@ class EndpointMgr(Gtk.Window):
|
||||
for ep in data["endpoints"]:
|
||||
provider = ep.get("provider_preset", "Custom")
|
||||
bt = label_for_backend(ep["backend_type"])
|
||||
self._store.append([ep["name"], provider, bt, ep.get("default_model", "")])
|
||||
persona = persona_short_key(ep)
|
||||
self._store.append([ep["name"], provider, bt, ep.get("default_model", ""), persona])
|
||||
|
||||
def _selected(self):
|
||||
sel = self._tree.get_selection()
|
||||
@@ -1337,7 +1423,7 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
self._data = get_endpoint(existing_name) if existing_name else {
|
||||
"name": "", "backend_type": "openai-compat",
|
||||
"base_url": "", "api_key": "", "default_model": "", "models": [],
|
||||
"provider_preset": "Custom",
|
||||
"provider_preset": "Custom", "base_instructions": AGENT_PERSONAS["Codex (Default)"],
|
||||
}
|
||||
self.set_default_size(480, 420)
|
||||
|
||||
@@ -1369,6 +1455,7 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
self._combo_type = Gtk.ComboBoxText()
|
||||
for val, lab in [("openai-compat", "OpenAI-compatible (needs proxy)"),
|
||||
("anthropic", "Anthropic (needs proxy)"),
|
||||
("command-code", "Command Code (needs proxy)"),
|
||||
("native", "Native OpenAI (no proxy)")]:
|
||||
self._combo_type.append(val, lab)
|
||||
bt = self._data.get("backend_type", "openai-compat")
|
||||
@@ -1382,6 +1469,25 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
self._entry_key.set_visibility(False)
|
||||
add_row(4, "API Key:", self._entry_key)
|
||||
|
||||
self._combo_persona = Gtk.ComboBoxText()
|
||||
self._persona_keys = list(AGENT_PERSONAS.keys())
|
||||
for pk in self._persona_keys:
|
||||
self._combo_persona.append_text(pk)
|
||||
cur_persona = persona_short_key(self._data)
|
||||
if cur_persona in self._persona_keys:
|
||||
self._combo_persona.set_active(self._persona_keys.index(cur_persona))
|
||||
else:
|
||||
self._combo_persona.set_active(0)
|
||||
self._combo_persona.connect("changed", lambda c: self._on_persona_changed())
|
||||
add_row(5, "Agent Persona:", self._combo_persona)
|
||||
|
||||
self._persona_preview = Gtk.Label()
|
||||
self._persona_preview.set_line_wrap(True)
|
||||
self._persona_preview.set_max_width_chars(60)
|
||||
self._persona_preview.set_markup(f"<small><i>{AGENT_PERSONAS['Codex (Default)'][:PERSONA_DISPLAY_LEN]}...</i></small>")
|
||||
self._on_persona_changed()
|
||||
grid.attach(self._persona_preview, 0, 6, 2, 1)
|
||||
|
||||
# Models
|
||||
mlbl = Gtk.Label(label="Models:", xalign=0)
|
||||
area.pack_start(mlbl, False, False, 4)
|
||||
@@ -1442,6 +1548,12 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
self.connect("response", self._on_response)
|
||||
self.show_all()
|
||||
|
||||
def _on_persona_changed(self):
|
||||
key = self._combo_persona.get_active_text()
|
||||
text = AGENT_PERSONAS.get(key, "")
|
||||
short = text[:PERSONA_DISPLAY_LEN] + ("..." if len(text) > PERSONA_DISPLAY_LEN else "")
|
||||
self._persona_preview.set_markup(f"<small><i>{short}</i></small>")
|
||||
|
||||
def _add_model(self):
|
||||
m = normalize_model_id(self._entry_model.get_text())
|
||||
if m:
|
||||
@@ -1574,9 +1686,11 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
self._show_error(f'Endpoint "{name}" already exists')
|
||||
return
|
||||
|
||||
persona_key = self._combo_persona.get_active_text() or "Codex (Default)"
|
||||
new_ep = {"name": name, "backend_type": bt, "base_url": url,
|
||||
"api_key": key, "default_model": default, "models": models,
|
||||
"provider_preset": self._combo_preset.get_active_text() or "Custom"}
|
||||
"provider_preset": self._combo_preset.get_active_text() or "Custom",
|
||||
"base_instructions": AGENT_PERSONAS.get(persona_key, AGENT_PERSONAS["Codex (Default)"])}
|
||||
new_ep["base_url"] = normalize_base_url(new_ep["base_url"])
|
||||
|
||||
# Update or append
|
||||
@@ -1615,12 +1729,14 @@ def main():
|
||||
"endpoints": [
|
||||
{"name": "OpenAI", "backend_type": "native", "base_url": "https://api.openai.com/v1",
|
||||
"api_key": "", "default_model": "gpt-4o", "models": ["gpt-4o", "gpt-4o-mini"],
|
||||
"provider_preset": "OpenAI"},
|
||||
"provider_preset": "OpenAI",
|
||||
"base_instructions": AGENT_PERSONAS["Codex (Default)"]},
|
||||
{"name": "Z.AI", "backend_type": "openai-compat",
|
||||
"base_url": "https://api.z.ai/api/coding/paas/v4",
|
||||
"api_key": "", "default_model": "glm-5.1",
|
||||
"models": ["glm-4.5", "glm-4.5-air", "glm-4.6", "glm-4.7", "glm-5", "glm-5-turbo", "glm-5.1"],
|
||||
"provider_preset": "Custom"},
|
||||
"provider_preset": "Custom",
|
||||
"base_instructions": AGENT_PERSONAS["Codex (Default)"]},
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ def load_config():
|
||||
p = argparse.ArgumentParser(description="Responses API translation proxy")
|
||||
p.add_argument("--config", help="JSON config file path")
|
||||
p.add_argument("--port", type=int, default=None)
|
||||
p.add_argument("--backend", default=None, choices=["openai-compat", "anthropic"])
|
||||
p.add_argument("--backend", default=None, choices=["openai-compat", "anthropic", "command-code"])
|
||||
p.add_argument("--target-url", default=None)
|
||||
p.add_argument("--api-key", default=None)
|
||||
p.add_argument("--models-file", default=None, help="JSON file with model list array")
|
||||
@@ -80,6 +80,7 @@ BACKEND = CONFIG["backend_type"]
|
||||
TARGET_URL = CONFIG["target_url"].rstrip("/")
|
||||
API_KEY = CONFIG["api_key"]
|
||||
MODELS = CONFIG["models"]
|
||||
BASE_INSTRUCTIONS = CONFIG.get("base_instructions", "")
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Shared helpers
|
||||
@@ -500,6 +501,119 @@ def an_stream_to_sse(stream, model, req_id):
|
||||
"response": {"id": resp_id, "object": "response", "model": model,
|
||||
"status": status, "created": int(time.time()), "output": completed}})
|
||||
|
||||
_DEFAULT_CC_CONFIG = {
|
||||
"workingDir": "/tmp",
|
||||
"date": "",
|
||||
"environment": "linux",
|
||||
"shell": "bash",
|
||||
"files": [],
|
||||
"structure": [],
|
||||
"isGitRepo": False,
|
||||
"currentBranch": "",
|
||||
"mainBranch": "",
|
||||
"gitStatus": "",
|
||||
"recentCommits": [],
|
||||
}
|
||||
|
||||
def _cc_config():
|
||||
cfg = dict(_DEFAULT_CC_CONFIG)
|
||||
cfg["date"] = time.strftime("%Y-%m-%d")
|
||||
return cfg
|
||||
|
||||
def cc_input_to_messages(input_data):
|
||||
return oa_input_to_messages(input_data)
|
||||
|
||||
def cc_convert_tools(tools):
|
||||
return oa_convert_tools(tools)
|
||||
|
||||
def cc_resp_to_responses(cc_lines, model, resp_id=None):
|
||||
text = ""
|
||||
usage = {}
|
||||
for line in cc_lines:
|
||||
try:
|
||||
d = json.loads(line)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
continue
|
||||
t = d.get("type", "")
|
||||
if t == "text-delta":
|
||||
text += d.get("text", "")
|
||||
elif t == "finish-step":
|
||||
u = d.get("usage", {})
|
||||
usage = {
|
||||
"input_tokens": u.get("inputTokens", 0),
|
||||
"output_tokens": u.get("outputTokens", 0),
|
||||
"total_tokens": u.get("inputTokens", 0) + u.get("outputTokens", 0),
|
||||
}
|
||||
outputs = []
|
||||
if text:
|
||||
outputs.append({"type": "message", "id": uid("msg"), "role": "assistant",
|
||||
"status": "completed",
|
||||
"content": [{"type": "output_text", "text": text, "annotations": []}]})
|
||||
return {"id": resp_id or uid("resp"), "object": "response", "created": int(time.time()),
|
||||
"model": model, "status": "completed", "output": outputs,
|
||||
"usage": {"input_tokens": usage.get("input_tokens", 0),
|
||||
"output_tokens": usage.get("output_tokens", 0),
|
||||
"total_tokens": usage.get("total_tokens", 0),
|
||||
"input_tokens_details": {"cached_tokens": 0}}}
|
||||
|
||||
def cc_stream_to_sse(cc_stream, model, req_id):
|
||||
resp_id = req_id or uid("resp")
|
||||
msg_id = uid("msg")
|
||||
text_buf = ""
|
||||
|
||||
yield emit("response.created", {"type": "response.created",
|
||||
"response": {"id": resp_id, "object": "response", "model": model,
|
||||
"status": "in_progress", "created": int(time.time()), "output": []}})
|
||||
yield emit("response.in_progress", {"type": "response.in_progress", "response": {"id": resp_id}})
|
||||
yield emit("response.output_item.added", {"type": "response.output_item.added",
|
||||
"item": {"type": "message", "id": msg_id, "role": "assistant", "status": "in_progress", "content": []}})
|
||||
yield emit("response.content_part.added", {"type": "response.content_part.added",
|
||||
"part": {"type": "output_text", "text": "", "annotations": []}, "item_id": msg_id})
|
||||
|
||||
total_usage = {}
|
||||
for raw in cc_stream:
|
||||
line = raw.decode("utf-8", errors="replace").strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
d = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
t = d.get("type", "")
|
||||
|
||||
if t == "text-delta":
|
||||
txt = d.get("text", "")
|
||||
if txt:
|
||||
text_buf += txt
|
||||
yield emit("response.output_text.delta", {"type": "response.output_text.delta",
|
||||
"delta": txt, "item_id": msg_id, "content_index": 0})
|
||||
|
||||
elif t == "finish-step":
|
||||
u = d.get("usage", {})
|
||||
total_usage = {
|
||||
"input_tokens": u.get("inputTokens", 0),
|
||||
"output_tokens": u.get("outputTokens", 0),
|
||||
"total_tokens": u.get("inputTokens", 0) + u.get("outputTokens", 0),
|
||||
}
|
||||
|
||||
if text_buf:
|
||||
yield emit("response.output_text.done", {"type": "response.output_text.done",
|
||||
"text": text_buf, "item_id": msg_id, "content_index": 0})
|
||||
yield emit("response.content_part.done", {"type": "response.content_part.done",
|
||||
"part": {"type": "output_text", "text": text_buf, "annotations": []}, "item_id": msg_id})
|
||||
yield emit("response.output_item.done", {"type": "response.output_item.done",
|
||||
"item": {"type": "message", "id": msg_id, "role": "assistant", "status": "completed",
|
||||
"content": [{"type": "output_text", "text": text_buf, "annotations": []}]}})
|
||||
|
||||
final_out = []
|
||||
if text_buf:
|
||||
final_out.append({"type": "message", "id": msg_id, "role": "assistant", "status": "completed",
|
||||
"content": [{"type": "output_text", "text": text_buf, "annotations": []}]})
|
||||
yield emit("response.completed", {"type": "response.completed",
|
||||
"response": {"id": resp_id, "object": "response", "model": model,
|
||||
"status": "completed", "created": int(time.time()), "output": final_out,
|
||||
"usage": total_usage}})
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# HTTP Server
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@@ -531,6 +645,8 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
|
||||
if BACKEND == "anthropic":
|
||||
self._handle_anthropic(body, model, stream)
|
||||
elif BACKEND == "command-code":
|
||||
self._handle_command_code(body, model, stream)
|
||||
else:
|
||||
self._handle_openai_compat(body, model, stream)
|
||||
|
||||
@@ -538,6 +654,8 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
input_data = body.get("input", "")
|
||||
messages = oa_input_to_messages(input_data)
|
||||
instructions = body.get("instructions", "").strip()
|
||||
if not instructions and BASE_INSTRUCTIONS:
|
||||
instructions = BASE_INSTRUCTIONS
|
||||
if instructions:
|
||||
messages.insert(0, {"role": "system", "content": instructions})
|
||||
chat_body = {"model": model, "messages": messages}
|
||||
@@ -571,6 +689,8 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
an_body = {"model": model, "messages": an_input_to_messages(input_data),
|
||||
"max_tokens": body.get("max_output_tokens", 8192)}
|
||||
instructions = body.get("instructions", "").strip()
|
||||
if not instructions and BASE_INSTRUCTIONS:
|
||||
instructions = BASE_INSTRUCTIONS
|
||||
if instructions:
|
||||
an_body["system"] = instructions
|
||||
for k in ("temperature", "top_p"):
|
||||
@@ -601,6 +721,76 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
lambda r: an_resp_to_responses(json.loads(r.read()), model),
|
||||
lambda s: an_stream_to_sse(s, model, body.get("request_id") or body.get("id")))
|
||||
|
||||
def _handle_command_code(self, body, model, stream):
|
||||
input_data = body.get("input", "")
|
||||
instructions = body.get("instructions", "").strip()
|
||||
messages = cc_input_to_messages(input_data)
|
||||
if not instructions and BASE_INSTRUCTIONS:
|
||||
instructions = BASE_INSTRUCTIONS
|
||||
if instructions:
|
||||
sys_msg = {"role": "system", "content": instructions}
|
||||
messages.insert(0, sys_msg)
|
||||
|
||||
cc_body = {
|
||||
"config": _cc_config(),
|
||||
"memory": "",
|
||||
"taste": "",
|
||||
"skills": "",
|
||||
"params": {
|
||||
"stream": True,
|
||||
"max_tokens": body.get("max_output_tokens", 64000),
|
||||
"temperature": body.get("temperature", 0.3),
|
||||
"messages": messages,
|
||||
"model": model,
|
||||
"tools": cc_convert_tools(body.get("tools")) or [],
|
||||
},
|
||||
"threadId": body.get("request_id") or uid("thread"),
|
||||
}
|
||||
|
||||
target = upstream_target(TARGET_URL, "/alpha/generate")
|
||||
fwd = forwarded_headers(self.headers, {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {API_KEY}",
|
||||
"Accept": "text/event-stream, application/json",
|
||||
}, browser_ua=True)
|
||||
print(f"[translate-proxy] POST {target} model={model} stream={stream} [command-code]", file=sys.stderr)
|
||||
req = urllib.request.Request(
|
||||
target,
|
||||
data=json.dumps(cc_body).encode(),
|
||||
headers=fwd,
|
||||
)
|
||||
|
||||
if stream:
|
||||
try:
|
||||
upstream = urllib.request.urlopen(req)
|
||||
except urllib.error.HTTPError as e:
|
||||
err = e.read().decode()
|
||||
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": err}})
|
||||
except Exception as e:
|
||||
return self.send_json(500, {"error": {"type": "proxy_error", "message": str(e)}})
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "text/event-stream")
|
||||
self.send_header("Cache-Control", "no-cache")
|
||||
self.send_header("Connection", "keep-alive")
|
||||
self.end_headers()
|
||||
for event in cc_stream_to_sse(upstream, model, body.get("request_id") or body.get("id")):
|
||||
self.wfile.write(event.encode("utf-8"))
|
||||
self.wfile.flush()
|
||||
else:
|
||||
try:
|
||||
upstream = urllib.request.urlopen(req)
|
||||
except urllib.error.HTTPError as e:
|
||||
err = e.read().decode()
|
||||
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": err}})
|
||||
except Exception as e:
|
||||
return self.send_json(500, {"error": {"type": "proxy_error", "message": str(e)}})
|
||||
|
||||
raw = upstream.read().decode()
|
||||
lines = raw.strip().split("\n")
|
||||
result = cc_resp_to_responses(lines, model)
|
||||
self.send_json(200, result)
|
||||
|
||||
def _forward(self, req, stream, model, nonstream_fn, stream_fn):
|
||||
try:
|
||||
upstream = urllib.request.urlopen(req)
|
||||
|
||||
Reference in New Issue
Block a user