fomext commited on
Commit
a8d2779
·
verified ·
1 Parent(s): 1744207

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -14
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import json
 
2
  import time
3
  import uuid
4
  from typing import Optional
@@ -78,56 +79,126 @@ def list_models() -> str:
78
  return json.dumps(result)
79
 
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  def chat_completions(
82
  messages_json: str,
83
  max_tokens: int = 512,
84
  temperature: float = 0.7,
85
  top_p: float = 0.9,
 
86
  ) -> str:
87
  """
88
  Non-streaming chat completions. Returns an OpenAI-compatible JSON string.
89
 
90
- messages_json: JSON array of {role, content} objects,
91
- e.g. '[{"role":"user","content":"Hello"}]'
92
 
93
- NOTE: Qwen3-Coder-30B-A3B-Instruct is non-thinking only.
94
- The enable_thinking parameter has been removed accordingly.
95
  """
96
  try:
97
  messages = json.loads(messages_json)
98
  except json.JSONDecodeError as e:
99
  return json.dumps({"error": f"Invalid messages_json: {e}"})
100
 
 
 
 
 
 
 
 
101
  try:
102
- hf_messages = [{"role": m["role"], "content": m["content"]} for m in messages]
103
- prompt = tokenizer.apply_chat_template(
104
- hf_messages,
105
- tokenize=False,
106
- add_generation_prompt=True,
107
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  except Exception as e:
109
  return json.dumps({"error": f"Prompt build failed: {e}"})
110
 
111
  gen_kwargs = dict(
112
  max_new_tokens=max_tokens,
113
- temperature=temperature,
114
  top_p=top_p,
115
  do_sample=True,
116
  pad_token_id=tokenizer.eos_token_id,
117
  )
118
 
119
  try:
120
- content = _generate_response(prompt, gen_kwargs)
121
  except Exception as e:
122
  return json.dumps({"error": f"Generation failed: {e}"})
123
 
124
  cid = f"chatcmpl-{uuid.uuid4().hex}"
 
 
 
 
 
 
 
 
 
125
  result = {
126
  "id": cid,
127
  "object": "chat.completion",
128
  "created": int(time.time()),
129
  "model": MODEL_ALIAS,
130
- "choices": [{"index": 0, "message": {"role": "assistant", "content": content}, "finish_reason": "stop"}],
131
  "usage": {"prompt_tokens": -1, "completion_tokens": -1, "total_tokens": -1},
132
  }
133
  return json.dumps(result)
@@ -179,11 +250,12 @@ You can also chat directly below.
179
  _cc_max_tokens = gr.Number(label="max_tokens", value=512)
180
  _cc_temp = gr.Number(label="temperature", value=0.7)
181
  _cc_top_p = gr.Number(label="top_p", value=0.9)
 
182
  _cc_out = gr.Textbox(label="result")
183
  _cc_btn = gr.Button("chat_completions")
184
  _cc_btn.click(
185
  fn=chat_completions,
186
- inputs=[_cc_messages, _cc_max_tokens, _cc_temp, _cc_top_p],
187
  outputs=[_cc_out],
188
  api_name="chat_completions",
189
  )
 
1
  import json
2
+ import re
3
  import time
4
  import uuid
5
  from typing import Optional
 
79
  return json.dumps(result)
80
 
81
 
82
+ # ---------------------------------------------------------------------------
83
+ # Tool call parsing (Hermes-style: <tool_call>{...}</tool_call>)
84
+ # ---------------------------------------------------------------------------
85
+
86
+ def _parse_tool_calls(text: str):
87
+ """
88
+ Detect and extract Hermes-style tool calls from model output.
89
+ Returns (tool_calls, remaining_content) where tool_calls is a list in
90
+ OpenAI format, or (None, text) if no tool calls are found.
91
+ """
92
+ pattern = r'<tool_call>(.*?)</tool_call>'
93
+ matches = re.findall(pattern, text, re.DOTALL)
94
+ if not matches:
95
+ return None, text
96
+
97
+ tool_calls = []
98
+ for match in matches:
99
+ try:
100
+ call = json.loads(match.strip())
101
+ tool_calls.append({
102
+ "id": f"call_{uuid.uuid4().hex[:24]}",
103
+ "type": "function",
104
+ "function": {
105
+ "name": call.get("name", ""),
106
+ "arguments": json.dumps(call.get("arguments", call.get("parameters", {}))),
107
+ },
108
+ })
109
+ except json.JSONDecodeError:
110
+ continue
111
+
112
+ if not tool_calls:
113
+ return None, text
114
+
115
+ remaining = re.sub(pattern, '', text, flags=re.DOTALL).strip()
116
+ return tool_calls, remaining or None
117
+
118
+
119
  def chat_completions(
120
  messages_json: str,
121
  max_tokens: int = 512,
122
  temperature: float = 0.7,
123
  top_p: float = 0.9,
124
+ tools_json: str = "",
125
  ) -> str:
126
  """
127
  Non-streaming chat completions. Returns an OpenAI-compatible JSON string.
128
 
129
+ messages_json: JSON array of {role, content} objects
130
+ tools_json: JSON array of OpenAI-format tool definitions (optional)
131
 
132
+ NOTE: Qwen3-Coder is non-thinking only; enable_thinking is not supported.
 
133
  """
134
  try:
135
  messages = json.loads(messages_json)
136
  except json.JSONDecodeError as e:
137
  return json.dumps({"error": f"Invalid messages_json: {e}"})
138
 
139
+ tools = None
140
+ if tools_json:
141
+ try:
142
+ tools = json.loads(tools_json)
143
+ except json.JSONDecodeError:
144
+ pass
145
+
146
  try:
147
+ hf_messages = []
148
+ for m in messages:
149
+ role = m["role"]
150
+ if role == "tool":
151
+ hf_messages.append({
152
+ "role": "tool",
153
+ "content": m.get("content", ""),
154
+ "tool_call_id": m.get("tool_call_id", ""),
155
+ })
156
+ elif role == "assistant" and m.get("tool_calls"):
157
+ hf_messages.append({
158
+ "role": "assistant",
159
+ "content": m.get("content") or "",
160
+ "tool_calls": m["tool_calls"],
161
+ })
162
+ else:
163
+ hf_messages.append({"role": role, "content": m.get("content", "")})
164
+
165
+ template_kwargs = dict(tokenize=False, add_generation_prompt=True)
166
+ if tools:
167
+ template_kwargs["tools"] = tools
168
+
169
+ prompt = tokenizer.apply_chat_template(hf_messages, **template_kwargs)
170
  except Exception as e:
171
  return json.dumps({"error": f"Prompt build failed: {e}"})
172
 
173
  gen_kwargs = dict(
174
  max_new_tokens=max_tokens,
175
+ temperature=max(temperature, 0.01),
176
  top_p=top_p,
177
  do_sample=True,
178
  pad_token_id=tokenizer.eos_token_id,
179
  )
180
 
181
  try:
182
+ raw = _generate_response(prompt, gen_kwargs)
183
  except Exception as e:
184
  return json.dumps({"error": f"Generation failed: {e}"})
185
 
186
  cid = f"chatcmpl-{uuid.uuid4().hex}"
187
+ tool_calls, content = _parse_tool_calls(raw)
188
+
189
+ if tool_calls:
190
+ message = {"role": "assistant", "content": content, "tool_calls": tool_calls}
191
+ finish_reason = "tool_calls"
192
+ else:
193
+ message = {"role": "assistant", "content": raw}
194
+ finish_reason = "stop"
195
+
196
  result = {
197
  "id": cid,
198
  "object": "chat.completion",
199
  "created": int(time.time()),
200
  "model": MODEL_ALIAS,
201
+ "choices": [{"index": 0, "message": message, "finish_reason": finish_reason}],
202
  "usage": {"prompt_tokens": -1, "completion_tokens": -1, "total_tokens": -1},
203
  }
204
  return json.dumps(result)
 
250
  _cc_max_tokens = gr.Number(label="max_tokens", value=512)
251
  _cc_temp = gr.Number(label="temperature", value=0.7)
252
  _cc_top_p = gr.Number(label="top_p", value=0.9)
253
+ _cc_tools = gr.Textbox(label="tools_json", value="")
254
  _cc_out = gr.Textbox(label="result")
255
  _cc_btn = gr.Button("chat_completions")
256
  _cc_btn.click(
257
  fn=chat_completions,
258
+ inputs=[_cc_messages, _cc_max_tokens, _cc_temp, _cc_top_p, _cc_tools],
259
  outputs=[_cc_out],
260
  api_name="chat_completions",
261
  )