···188188 """
189189 if "<think>" in response and "</think>" in response:
190190 return response.split("<think>")[1].split("</think>")[0]
191191+ elif "</think>" in response:
192192+ return response.split("</think>")[0]
191193 else:
192194 return ""
193195
+1
server/system_prompt.txt
···4040```
41414242**CRITICAL: Always close ALL tags! Missing </think>, </python>, or </reply> will cause errors!**
4343+**CRITICAL: Think block MUST have opening <think> and closing </think>. This block should not only have </think>
43444445**NEVER:**
4546- Skip the `<think>` block
+134-15
src/runner/mlx.rs
···11+use crate::core::modelfile::Modelfile;
12use anyhow::{Context, Result};
33+use futures_util::StreamExt;
44+use owo_colors::OwoColorize;
25use reqwest::Client;
36use serde_json::{Value, json};
47use std::io::Write;
58use std::path::PathBuf;
69use std::process::Stdio;
1010+use std::str::FromStr;
711use std::{env, fs};
812use std::{io, process::Command};
99-1010-use crate::core::modelfile::Modelfile;
1313+pub struct ChatResponse {
1414+ think: String,
1515+ reply: String,
1616+ code: String,
1717+}
11181219pub async fn run(modelfile: Modelfile) {
1320 let model = modelfile.from.as_ref().unwrap();
···129136 let modelname = modelfile.from.as_ref().unwrap();
130137 load_model(modelname, &memory_path).await.unwrap();
131138 println!("Running in interactive mode");
139139+ // TODO: Handle "enter" key press or any key press when repl is processing an input
132140 loop {
133141 print!(">> ");
134142 stdout.flush().unwrap();
···141149 break;
142150 }
143151 _ => {
144144- if let Ok(response) = chat(input, modelname).await {
145145- println!(">> {}", response)
146146- } else {
147147- println!(">> failed to respond")
152152+ let mut remaining_count = 6;
153153+ let mut g_reply: String = "".to_owned();
154154+ let mut python_code: String = "".to_owned();
155155+ loop {
156156+ if remaining_count > 0 {
157157+ let chat_start = if remaining_count == 6 { true } else { false };
158158+ if let Ok(response) = chat(input, modelname, chat_start, &python_code).await
159159+ {
160160+ if response.reply.is_empty() {
161161+ if !response.code.is_empty() {
162162+ python_code = response.code;
163163+ }
164164+ remaining_count = remaining_count - 1;
165165+ } else {
166166+ g_reply = response.reply.clone();
167167+ println!("\n>> {}", response.reply.trim());
168168+ break;
169169+ }
170170+ } else {
171171+ println!("\n>> failed to respond");
172172+ break;
173173+ }
174174+ }
175175+ }
176176+ if g_reply.is_empty() {
177177+ println!(">> No reply")
148178 }
149179 }
150180 }
···178208 }
179209}
180210181181-async fn chat(input: &str, model_name: &str) -> Result<String, String> {
211211+async fn chat(
212212+ input: &str,
213213+ model_name: &str,
214214+ chat_start: bool,
215215+ python_code: &str,
216216+) -> Result<ChatResponse, String> {
182217 let client = Client::new();
218218+183219 let body = json!({
184220 "model": model_name,
221221+ "chat_start": chat_start,
222222+ "stream": true,
223223+ "python_code": python_code,
185224 "messages": [{"role": "user", "content": input}]
186225 });
187226 let res = client
···190229 .send()
191230 .await
192231 .unwrap();
232232+233233+ let mut stream = res.bytes_stream();
234234+ let mut accumulated = String::new();
235235+ let mut chat_response = ChatResponse {
236236+ think: String::new(),
237237+ reply: String::new(),
238238+ code: String::new(),
239239+ };
240240+ // let mut inside_python = false;
241241+ // let mut tag_buffer = String::new();
242242+ print!("\n");
243243+ while let Some(chunk) = stream.next().await {
244244+ let chunk = chunk.unwrap();
245245+ let s = String::from_utf8_lossy(&chunk);
246246+ for line in s.lines() {
247247+ if !line.starts_with("data: ") {
248248+ continue;
249249+ }
250250+251251+ let data = line.trim_start_matches("data: ");
252252+253253+ if data == "[DONE]" {
254254+ chat_response = convert_to_chat_response(&accumulated);
255255+ return Ok(chat_response);
256256+ }
257257+ // Parse JSON
258258+ let v: Value = serde_json::from_str(data).unwrap();
259259+ if let Some(delta) = v["choices"][0]["delta"]["content"].as_str() {
260260+ accumulated.push_str(delta);
261261+ print!("{}", delta.dimmed());
262262+ use std::io::Write;
263263+ std::io::stdout().flush().ok();
264264+ }
265265+ }
266266+ }
193267 // println!("{:?}", res);
194194- if res.status() == 200 {
195195- let text = res.text().await.unwrap();
196196- let v: Value = serde_json::from_str(&text).unwrap();
197197- let content = v["choices"][0]["message"]["content"]
198198- .as_str()
199199- .unwrap_or("<no content>");
200200- Ok(content.to_owned())
268268+ // if res.status() == 200 {
269269+ // let text = res.text().await.unwrap();
270270+ // let v: Value = serde_json::from_str(&text).unwrap();
271271+ // let content = v["choices"][0]["message"]["content"]
272272+ // .as_str()
273273+ // .unwrap_or("<no content>");
274274+275275+ // // Ok(convert_to_chat_response(content))
276276+ // } else {
277277+ // // Err(String::from("request failed"))
278278+ // }
279279+ // unimplemented!()
280280+ Err(String::from("request failed"))
281281+}
282282+283283+fn convert_to_chat_response(content: &str) -> ChatResponse {
284284+ // content.split()
285285+ ChatResponse {
286286+ think: extract_think(content),
287287+ reply: extract_reply(content),
288288+ code: extract_python(content),
289289+ }
290290+}
291291+292292+fn extract_reply(content: &str) -> String {
293293+ if content.contains("<reply>") && content.contains("</reply>") {
294294+ let list_a = content.split("<reply>").collect::<Vec<&str>>();
295295+ let list_b = list_a[1].split("</reply>").collect::<Vec<&str>>();
296296+ list_b[0].to_owned()
201297 } else {
202202- Err(String::from("request failed"))
298298+ "".to_owned()
299299+ }
300300+}
301301+302302+fn extract_python(content: &str) -> String {
303303+ if content.contains("<python>") && content.contains("</python>") {
304304+ let list_a = content.split("<python>").collect::<Vec<&str>>();
305305+ let list_b = list_a[1].split("</python>").collect::<Vec<&str>>();
306306+ list_b[0].to_owned()
307307+ } else {
308308+ "".to_owned()
309309+ }
310310+}
311311+312312+fn extract_think(content: &str) -> String {
313313+ if content.contains("<think>") && content.contains("</think>") {
314314+ let list_a = content.split("<think>").collect::<Vec<&str>>();
315315+ let list_b = list_a[1].split("</think>").collect::<Vec<&str>>();
316316+ list_b[0].to_owned()
317317+ } else if content.contains("</think") {
318318+ let list_a = content.split("</think>").collect::<Vec<&str>>();
319319+ list_a[0].to_owned()
320320+ } else {
321321+ "".to_owned()
203322 }
204323}
205324