os-odyssey commited on
Commit
8bb82ab
·
verified ·
1 Parent(s): 99f25f0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +181 -130
app.py CHANGED
@@ -1,161 +1,212 @@
1
  # app.py
2
- # Simple coding assistant with analyze-and-rewrite workflow
3
- # Brand: odyssey
4
-
5
  import os
 
6
  import requests
7
  import gradio as gr
 
8
 
9
- # ===== DEFAULTS =====
10
- DEFAULT_MODEL = "stabilityai/stable-code-instruct-3b"
11
- DEFAULT_MAX_NEW_TOKENS = 512
12
- DEFAULT_TEMPERATURE = 0.2
13
- DEFAULT_TOP_P = 0.95
14
- DEFAULT_TOP_K = 50
15
-
16
  HF_INFERENCE_URL = "https://api-inference.huggingface.co/models/{}"
17
-
18
- # ===== Helper: call Hugging Face text-generation inference API =====
19
- def hf_text_generation(model: str, prompt: str, hf_token: str, params: dict):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  url = HF_INFERENCE_URL.format(model)
21
- headers = {"Authorization": f"Bearer {hf_token}"}
22
  payload = {"inputs": prompt, "parameters": params}
23
- r = requests.post(url, headers=headers, json=payload, timeout=120)
24
- r.raise_for_status()
25
- data = r.json()
26
-
27
- if isinstance(data, dict) and "error" in data:
28
- raise RuntimeError(f"Hugging Face error: {data['error']}")
29
 
30
- # Common response shapes:
31
- # 1) [{"generated_text": "..."}]
32
- # 2) [{"some_key": ...}] or string-like results
33
- if isinstance(data, list) and len(data) > 0:
34
- first = data[0]
35
- if isinstance(first, dict) and "generated_text" in first:
36
- return first["generated_text"]
37
- return str(first)
38
-
39
- return str(data)
40
-
41
- # ===== Analysis & rewrite instruction prompt =====
42
- ANALYSIS_INSTRUCTION = """
43
- You are a senior code reviewer and refactorer.
44
- Step 1: Analyze the following input (it can be source code or a prompt). Provide concise bullet points: bugs, edge-cases, security concerns, performance issues, missing pieces, and concrete suggestions.
45
- Step 2: Produce an optimized, rewritten version of the code or a clarified prompt that is ready-to-run or ready-to-feed into a code generation model.
46
-
47
- Return the output in two clearly marked sections:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  ===ANALYSIS===
50
- - bullet points...
51
 
52
  ===REWRITTEN===
53
- <optimized code or clarified prompt>
54
-
55
- Return only these two sections and nothing else.
56
  """
57
 
58
- def analyze_and_rewrite_via_api(model, user_input, hf_token, analysis_max_tokens=512, gen_max_tokens=512, temperature=0.15):
59
  prompt = ANALYSIS_INSTRUCTION + "\n\nINPUT:\n" + user_input + "\n\n"
60
  params = {
61
- "max_new_tokens": analysis_max_tokens,
62
  "temperature": temperature,
63
- "top_p": DEFAULT_TOP_P,
64
- "top_k": DEFAULT_TOP_K,
65
  "return_full_text": False
66
  }
67
- raw = hf_text_generation(model, prompt, hf_token, params)
 
68
 
69
- if "===ANALYSIS===" in raw and "===REWRITTEN===" in raw:
70
- analysis = raw.split("===ANALYSIS===")[1].split("===REWRITTEN===")[0].strip()
71
- rewritten = raw.split("===REWRITTEN===")[1].strip()
72
- return analysis, rewritten, raw
73
-
74
- # Fallback: return full raw as analysis if markers are missing
75
- return raw, "", raw
76
-
77
- def generate_code_via_api(model, prompt, hf_token, max_new_tokens=256, temperature=0.2, top_p=0.95, top_k=50):
78
  params = {
79
- "max_new_tokens": int(max_new_tokens),
80
- "temperature": float(temperature),
81
- "top_p": float(top_p),
82
- "top_k": int(top_k),
83
  "return_full_text": False
84
  }
85
- return hf_text_generation(model, prompt, hf_token, params)
86
-
87
- # ===== Gradio UI =====
88
- with gr.Blocks(title="odyssey Coding Assistant & Rewriter") as demo:
89
- gr.Markdown("## odyssey — Coding Assistant and Rewriter (Qwen Coder)")
90
- gr.Markdown(
91
- "This tool lets you customize model, token, temperature and output length. "
92
- "Enable 'Analyze Rewrite' to have the model first analyze the input and then produce an optimized rewrite."
93
- )
94
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  with gr.Row():
96
  with gr.Column(scale=2):
97
- user_input = gr.Textbox(
98
- label="Input (code or prompt)",
99
- lines=8,
100
- placeholder="Paste the code you want reviewed/refactored or the prompt you want optimized..."
101
- )
102
- thinking_toggle = gr.Checkbox(label="Analyze Rewrite (thinking mode)", value=True)
103
- model_input = gr.Textbox(label="Hugging Face model name", value=DEFAULT_MODEL)
104
- use_remote = gr.Checkbox(label="Use Hugging Face Inference API (recommended)", value=True)
105
- hf_token_input = gr.Textbox(
106
- label="Hugging Face API token (leave empty to use HF_API_TOKEN secret)",
107
- type="password"
108
- )
109
- max_tokens_input = gr.Slider(
110
- minimum=64, maximum=2048, step=16,
111
- label="max_new_tokens", value=DEFAULT_MAX_NEW_TOKENS
112
- )
113
- temp_input = gr.Slider(
114
- minimum=0.0, maximum=1.0, step=0.01,
115
- label="temperature", value=DEFAULT_TEMPERATURE
116
- )
117
- top_p_input = gr.Slider(minimum=0.1, maximum=1.0, step=0.05, label="top_p", value=DEFAULT_TOP_P)
118
- top_k_input = gr.Slider(minimum=0, maximum=200, step=1, label="top_k", value=DEFAULT_TOP_K)
119
- generate_btn = gr.Button("Run")
120
-
121
  with gr.Column(scale=2):
122
- analysis_out = gr.Textbox(label="Model Analysis", lines=8)
123
- rewritten_out = gr.Textbox(label="Rewritten Code / Prompt", lines=12)
124
- normal_out = gr.Textbox(label="Normal Generation Output", lines=12)
125
-
126
- def on_generate(inp, thinking, model_name, use_remote_flag, token, max_new_toks, temp, top_p, top_k):
127
- hf_token = token or os.environ.get("HF_API_TOKEN")
128
- if use_remote_flag and not hf_token:
129
- return "ERROR: Please provide a Hugging Face API token or set HF_API_TOKEN in Secrets.", "", ""
130
-
131
- try:
132
- if thinking:
133
- analysis, rewritten, raw = analyze_and_rewrite_via_api(
134
- model_name, inp, hf_token,
135
- analysis_max_tokens=min(1024, max_new_toks // 2),
136
- gen_max_tokens=max_new_toks,
137
- temperature=temp
138
- )
139
- final_source = rewritten if rewritten.strip() else inp
140
- normal_generation = generate_code_via_api(
141
- model_name, final_source, hf_token,
142
- max_new_tokens=max_new_toks,
143
- temperature=temp,
144
- top_p=top_p, top_k=top_k
145
- )
146
- return analysis, rewritten, normal_generation
147
- else:
148
- out = generate_code_via_api(model_name, inp, hf_token,
149
- max_new_tokens=max_new_toks, temperature=temp,
150
- top_p=top_p, top_k=top_k)
151
- return "", "", out
152
- except Exception as e:
153
- return f"Model execution error: {str(e)}", "", ""
154
-
155
- generate_btn.click(
156
  on_generate,
157
- inputs=[user_input, thinking_toggle, model_input, use_remote, hf_token_input, max_tokens_input, temp_input, top_p_input, top_k_input],
158
- outputs=[analysis_out, rewritten_out, normal_out]
 
159
  )
160
 
161
  if __name__ == "__main__":
 
1
  # app.py
2
+ # Odyssey Coder enhanced: multi-file output + analyze & rewrite + download
 
 
3
  import os
4
+ import tempfile
5
  import requests
6
  import gradio as gr
7
+ from typing import Tuple
8
 
9
+ # -------------------
10
+ # Configuration
11
+ # -------------------
12
+ DEFAULT_MODEL = "bigcode/starcoder2-3b" # پیش‌فرض سبک‌تر
 
 
 
13
  HF_INFERENCE_URL = "https://api-inference.huggingface.co/models/{}"
14
+ HF_TOKEN = os.environ.get("HF_API_TOKEN") # از Secrets خوانده می‌شود
15
+
16
+ # Preset model choices (user can override with custom model field)
17
+ PRESET_MODELS = [
18
+ "bigcode/starcoder2-3b",
19
+ "Salesforce/codegen-2B-multi",
20
+ "huggingface/CodeParrot-small",
21
+ "google/flan-t5-small" # fallback NLP model (not code-specialized)
22
+ ]
23
+
24
+ # Allowed file extensions for download
25
+ ALLOWED_EXTS = [".py", ".js", ".html", ".md", ".txt"]
26
+
27
+ # -------------------
28
+ # Helper: call HF Inference API
29
+ # -------------------
30
+ def hf_text_generation(model: str, prompt: str, hf_token: str, params: dict) -> Tuple[bool, str]:
31
+ """
32
+ Returns (ok, text_or_error)
33
+ ok=True => text result
34
+ ok=False => error message
35
+ """
36
  url = HF_INFERENCE_URL.format(model)
37
+ headers = {"Authorization": f"Bearer {hf_token}"} if hf_token else {}
38
  payload = {"inputs": prompt, "parameters": params}
 
 
 
 
 
 
39
 
40
+ try:
41
+ r = requests.post(url, headers=headers, json=payload, timeout=120)
42
+ r.raise_for_status()
43
+ data = r.json()
44
+ # typical shapes: [{"generated_text": "..."}]
45
+ if isinstance(data, list) and len(data) > 0:
46
+ first = data[0]
47
+ if isinstance(first, dict) and "generated_text" in first:
48
+ return True, first["generated_text"]
49
+ # sometimes models return plain text or dict with other keys
50
+ return True, str(first)
51
+ return True, str(data)
52
+ except requests.exceptions.HTTPError as he:
53
+ status = he.response.status_code if he.response is not None else None
54
+ if status == 401:
55
+ return False, "Authentication error: invalid or missing HF_API_TOKEN."
56
+ if status == 403:
57
+ return False, "Permission error: the selected model or resource is restricted."
58
+ if status == 404:
59
+ return False, "Model not found (404). Check the model id."
60
+ if status == 410:
61
+ return False, "Model endpoint not available (410 Gone). Try another model."
62
+ # generic
63
+ return False, f"HTTP error {status}: {he.response.text if he.response is not None else str(he)}"
64
+ except requests.exceptions.RequestException as e:
65
+ return False, f"Request error: {str(e)}"
66
+
67
+ # -------------------
68
+ # Core: analyze & rewrite
69
+ # -------------------
70
+ ANALYSIS_INSTRUCTION = """You are an expert senior code reviewer and refactorer.
71
+ Step 1: Provide concise bullet-point analysis: bugs, edge-cases, security issues, missing pieces, and suggestions.
72
+ Step 2: Produce an optimized, runnable, and well-commented version of the code or a clarified prompt.
73
+
74
+ Return ONLY two sections marked exactly as below:
75
 
76
  ===ANALYSIS===
77
+ - ...
78
 
79
  ===REWRITTEN===
80
+ <rewritten code or prompt>
 
 
81
  """
82
 
83
+ def analyze_and_rewrite(model: str, user_input: str, hf_token: str, max_new_tokens: int, temperature: float, top_p: float, top_k: int):
84
  prompt = ANALYSIS_INSTRUCTION + "\n\nINPUT:\n" + user_input + "\n\n"
85
  params = {
86
+ "max_new_tokens": max_new_tokens,
87
  "temperature": temperature,
88
+ "top_p": top_p,
89
+ "top_k": top_k,
90
  "return_full_text": False
91
  }
92
+ ok, resp = hf_text_generation(model, prompt, hf_token, params)
93
+ return ok, resp
94
 
95
+ def normal_generate(model: str, user_input: str, hf_token: str, max_new_tokens: int, temperature: float, top_p: float, top_k: int):
 
 
 
 
 
 
 
 
96
  params = {
97
+ "max_new_tokens": max_new_tokens,
98
+ "temperature": temperature,
99
+ "top_p": top_p,
100
+ "top_k": top_k,
101
  "return_full_text": False
102
  }
103
+ return hf_text_generation(model, user_input, hf_token, params)
104
+
105
+ # -------------------
106
+ # Utility: write output to a file and return path
107
+ # -------------------
108
+ def write_temp_file(content: str, filename: str, ext: str) -> str:
109
+ if not ext.startswith("."):
110
+ ext = "." + ext
111
+ # safe filename
112
+ safe_name = "".join(c for c in filename if c.isalnum() or c in ("-", "_", "." )).strip()
113
+ if not safe_name:
114
+ safe_name = "output"
115
+ if not safe_name.endswith(ext):
116
+ safe_name = safe_name + ext
117
+ tmpdir = tempfile.mkdtemp()
118
+ path = os.path.join(tmpdir, safe_name)
119
+ with open(path, "w", encoding="utf-8") as f:
120
+ f.write(content)
121
+ return path
122
+
123
+ # -------------------
124
+ # Gradio app logic
125
+ # -------------------
126
+ def on_generate(inp, mode_analyze, preset_model, custom_model, use_preset, hf_token_input,
127
+ max_new_tokens, temperature, top_p, top_k, file_ext, filename, make_download):
128
+ # Resolve model
129
+ model = preset_model if use_preset else (custom_model.strip() or DEFAULT_MODEL)
130
+ hf_token = hf_token_input or os.environ.get("HF_API_TOKEN")
131
+ if not hf_token:
132
+ return ("ERROR: No HF token set. Add HF_API_TOKEN to Space Secrets or enter token here.", "", "", None)
133
+
134
+ if mode_analyze:
135
+ ok, resp = analyze_and_rewrite(model, inp, hf_token, max_new_tokens, temperature, top_p, top_k)
136
+ if not ok:
137
+ return (f"Error during analysis: {resp}", "", "", None)
138
+ # parse sections
139
+ if "===ANALYSIS===" in resp and "===REWRITTEN===" in resp:
140
+ analysis = resp.split("===ANALYSIS===")[1].split("===REWRITTEN===")[0].strip()
141
+ rewritten = resp.split("===REWRITTEN===")[1].strip()
142
+ else:
143
+ # fallback: show full response in analysis
144
+ analysis = resp
145
+ rewritten = ""
146
+ # If rewritten exists, generate final output from rewritten (normal generate)
147
+ final_prompt = rewritten if rewritten.strip() else inp
148
+ ok2, gen = normal_generate(model, final_prompt, hf_token, max_new_tokens, temperature, top_p, top_k)
149
+ if not ok2:
150
+ return (f"Error during generation: {gen}", analysis, rewritten, None)
151
+ else:
152
+ # normal generation
153
+ ok, gen = normal_generate(model, inp, hf_token, max_new_tokens, temperature, top_p, top_k)
154
+ if not ok:
155
+ return (f"Error during generation: {gen}", "", "", None)
156
+ analysis = ""
157
+ rewritten = ""
158
+ gen = gen
159
+
160
+ # If user asked for download, create file
161
+ file_path = None
162
+ if make_download:
163
+ ext = file_ext if file_ext.startswith(".") else f".{file_ext}"
164
+ if ext not in ALLOWED_EXTS:
165
+ return ("Invalid file extension selected.", analysis, rewritten, None)
166
+ file_path = write_temp_file(gen, filename or "odyssey_output", ext)
167
+
168
+ return ("OK", analysis, rewritten, file_path)
169
+
170
+ # -------------------
171
+ # Build Gradio UI
172
+ # -------------------
173
+ with gr.Blocks(title="odyssey — Enhanced Coder (multi-file)") as demo:
174
+ gr.Markdown("## odyssey — Enhanced Coding Assistant\nAnalyze → Rewrite mode, choose output file type and download the generated file.")
175
  with gr.Row():
176
  with gr.Column(scale=2):
177
+ inp = gr.Textbox(label="Input (code or prompt)", lines=8, placeholder="Paste code or prompt...")
178
+ mode_analyze = gr.Checkbox(label="Analyze Rewrite (recommended)", value=True)
179
+ # model selection
180
+ preset_model = gr.Dropdown(choices=PRESET_MODELS, value=PRESET_MODELS[0], label="Preset model")
181
+ use_preset = gr.Checkbox(label="Use preset model (otherwise use custom model below)", value=True)
182
+ custom_model = gr.Textbox(label="Custom model id (optional)", placeholder="e.g. username/model-name")
183
+ hf_token_input = gr.Textbox(label="HuggingFace API Token (leave empty to use HF_API_TOKEN secret)", type="password")
184
+
185
+ with gr.Row():
186
+ max_new_tokens = gr.Slider(64, 2048, value=512, step=64, label="max_new_tokens")
187
+ temperature = gr.Slider(0.0, 1.0, value=0.2, step=0.01, label="temperature")
188
+ with gr.Row():
189
+ top_p = gr.Slider(0.1, 1.0, value=0.95, step=0.05, label="top_p")
190
+ top_k = gr.Slider(0, 200, value=50, step=1, label="top_k")
191
+
192
+ gr.Markdown("### Output file options")
193
+ file_ext = gr.Dropdown(choices=[e.lstrip(".") for e in ALLOWED_EXTS], value="py", label="File extension")
194
+ filename = gr.Textbox(label="Filename (without extension)", value="odyssey_output")
195
+ make_download = gr.Checkbox(label="Create downloadable file", value=True)
196
+
197
+ btn = gr.Button("Generate")
 
 
 
198
  with gr.Column(scale=2):
199
+ status_out = gr.Textbox(label="Status / Errors", lines=2)
200
+ analysis_out = gr.Textbox(label="Analysis (if analyze mode)", lines=8)
201
+ rewritten_out = gr.Textbox(label="Rewritten code / prompt", lines=12)
202
+ gen_out = gr.Textbox(label="Final generated text", lines=14)
203
+ file_down = gr.File(label="Download generated file")
204
+
205
+ btn.click(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  on_generate,
207
+ inputs=[inp, mode_analyze, preset_model, custom_model, use_preset, hf_token_input,
208
+ max_new_tokens, temperature, top_p, top_k, file_ext, filename, make_download],
209
+ outputs=[status_out, analysis_out, rewritten_out, file_down]
210
  )
211
 
212
  if __name__ == "__main__":