gr8monk3ys commited on
Commit
9fb23ed
·
verified ·
1 Parent(s): a55100e

Sync card and scripts from the monorepo

Browse files
Files changed (4) hide show
  1. README.md +9 -12
  2. app.py +20 -17
  3. hf_client.py +121 -0
  4. requirements.txt +3 -3
README.md CHANGED
@@ -4,19 +4,12 @@ emoji: 💻
4
  colorFrom: green
5
  colorTo: blue
6
  sdk: gradio
7
- sdk_version: 5.50.0
8
  python_version: "3.10"
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
  short_description: AI-powered code explanation and documentation
13
- tags:
14
- - code
15
- - education
16
- - llm
17
- - developer-tools
18
- models:
19
- - mistralai/Mistral-7B-Instruct-v0.3
20
  ---
21
 
22
  # Code Explainer
@@ -36,10 +29,10 @@ Choose your expertise level:
36
 
37
  ### Structured Output
38
  Each explanation includes:
39
- - **Overview** - What the code does at a high level
40
- - **Line-by-Line Breakdown** - Detailed explanation of each part
41
  - **Key Concepts** - Important programming concepts used
42
- - **Potential Issues** - Common pitfalls or improvements
43
 
44
  ### Syntax Highlighting
45
  Code is displayed with proper syntax highlighting for readability.
@@ -55,7 +48,7 @@ Code is displayed with proper syntax highlighting for readability.
55
 
56
  | Component | Technology |
57
  |-----------|------------|
58
- | Web Framework | Gradio 5.9.1 |
59
  | AI Model | Mistral-7B via HuggingFace Inference API |
60
  | Code Formatting | Pygments |
61
 
@@ -72,6 +65,10 @@ Code is displayed with proper syntax highlighting for readability.
72
  - Complex multi-file projects may need to be explained piece by piece
73
  - AI explanations should be verified for critical code
74
 
 
 
 
 
75
  ## License
76
 
77
  MIT
 
4
  colorFrom: green
5
  colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 5.31.0
8
  python_version: "3.10"
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
  short_description: AI-powered code explanation and documentation
 
 
 
 
 
 
 
13
  ---
14
 
15
  # Code Explainer
 
29
 
30
  ### Structured Output
31
  Each explanation includes:
32
+ - **Overview** - A brief summary of what the code does
33
+ - **Step-by-Step Breakdown** - Section-by-section explanation of the code
34
  - **Key Concepts** - Important programming concepts used
35
+ - **Potential Improvements** - Suggested improvements, best practices, or issues to be aware of
36
 
37
  ### Syntax Highlighting
38
  Code is displayed with proper syntax highlighting for readability.
 
48
 
49
  | Component | Technology |
50
  |-----------|------------|
51
+ | Web Framework | Gradio 5.31.0 |
52
  | AI Model | Mistral-7B via HuggingFace Inference API |
53
  | Code Formatting | Pygments |
54
 
 
65
  - Complex multi-file projects may need to be explained piece by piece
66
  - AI explanations should be verified for critical code
67
 
68
+ ## Configuration
69
+
70
+ This Space calls the **HuggingFace Inference API** (Mistral-7B). It requires an `HF_TOKEN` secret — a token with inference access — set in **Space Settings → Secrets**. Without it, generation will fail.
71
+
72
  ## License
73
 
74
  MIT
app.py CHANGED
@@ -2,12 +2,15 @@
2
  Code Explainer - AI-powered code explanation using HuggingFace Inference API.
3
  """
4
 
 
 
5
  import gradio as gr
6
- from huggingface_hub import InferenceClient
7
  from pygments import highlight
8
  from pygments.lexers import get_lexer_by_name, guess_lexer
9
  from pygments.formatters import HtmlFormatter
10
 
 
 
11
  # ---------------------------------------------------------------------------
12
  # Configuration
13
  # ---------------------------------------------------------------------------
@@ -43,7 +46,7 @@ EXPLANATION_LEVELS = {
43
  # Initialize client
44
  # ---------------------------------------------------------------------------
45
 
46
- client = InferenceClient(MODEL_ID)
47
 
48
  # ---------------------------------------------------------------------------
49
  # Utility functions
@@ -77,7 +80,7 @@ def format_code_html(code: str, language: str) -> str:
77
  formatter = HtmlFormatter(style="monokai", noclasses=True)
78
  return highlight(code, lexer, formatter)
79
  except Exception:
80
- return f"<pre><code>{code}</code></pre>"
81
 
82
 
83
  # ---------------------------------------------------------------------------
@@ -96,12 +99,12 @@ def explain_code(code: str, language: str, level: str) -> tuple[str, str]:
96
 
97
  # Build prompt
98
  level_instruction = EXPLANATION_LEVELS.get(level, EXPLANATION_LEVELS["Intermediate"])
 
99
 
100
- prompt = f"""You are an expert programming tutor. {level_instruction}
 
101
 
102
- Here is the code to explain:
103
-
104
- ```{detected_lang.lower()}
105
  {code}
106
  ```
107
 
@@ -122,16 +125,17 @@ Suggest any improvements, best practices, or potential issues to be aware of.
122
  Keep your explanation clear, accurate, and educational."""
123
 
124
  try:
125
- # Call the inference API
126
- response = client.text_generation(
127
- prompt,
128
- max_new_tokens=1500,
 
 
 
129
  temperature=0.7,
130
  top_p=0.95,
131
- do_sample=True,
132
  )
133
-
134
- explanation = response.strip()
135
 
136
  # Add language badge
137
  explanation = f"**Detected Language:** `{detected_lang}`\n\n---\n\n{explanation}"
@@ -141,9 +145,8 @@ Keep your explanation clear, accurate, and educational."""
141
 
142
  return explanation, formatted_code
143
 
144
- except Exception as e:
145
- error_msg = f"Error generating explanation: {str(e)}"
146
- return error_msg, format_code_html(code, detected_lang)
147
 
148
 
149
  # ---------------------------------------------------------------------------
 
2
  Code Explainer - AI-powered code explanation using HuggingFace Inference API.
3
  """
4
 
5
+ import html
6
+
7
  import gradio as gr
 
8
  from pygments import highlight
9
  from pygments.lexers import get_lexer_by_name, guess_lexer
10
  from pygments.formatters import HtmlFormatter
11
 
12
+ from hf_client import InferenceError, make_client, with_retry
13
+
14
  # ---------------------------------------------------------------------------
15
  # Configuration
16
  # ---------------------------------------------------------------------------
 
46
  # Initialize client
47
  # ---------------------------------------------------------------------------
48
 
49
+ client = make_client(MODEL_ID)
50
 
51
  # ---------------------------------------------------------------------------
52
  # Utility functions
 
80
  formatter = HtmlFormatter(style="monokai", noclasses=True)
81
  return highlight(code, lexer, formatter)
82
  except Exception:
83
+ return f"<pre><code>{html.escape(code)}</code></pre>"
84
 
85
 
86
  # ---------------------------------------------------------------------------
 
99
 
100
  # Build prompt
101
  level_instruction = EXPLANATION_LEVELS.get(level, EXPLANATION_LEVELS["Intermediate"])
102
+ lexer_hint = detected_lang.split()[0].lower() if detected_lang.strip() else ""
103
 
104
+ system_prompt = f"You are an expert programming tutor. {level_instruction}"
105
+ user_prompt = f"""Explain the following code.
106
 
107
+ ```{lexer_hint}
 
 
108
  {code}
109
  ```
110
 
 
125
  Keep your explanation clear, accurate, and educational."""
126
 
127
  try:
128
+ completion = with_retry(
129
+ client.chat_completion,
130
+ messages=[
131
+ {"role": "system", "content": system_prompt},
132
+ {"role": "user", "content": user_prompt},
133
+ ],
134
+ max_tokens=1500,
135
  temperature=0.7,
136
  top_p=0.95,
 
137
  )
138
+ explanation = completion.choices[0].message.content.strip()
 
139
 
140
  # Add language badge
141
  explanation = f"**Detected Language:** `{detected_lang}`\n\n---\n\n{explanation}"
 
145
 
146
  return explanation, formatted_code
147
 
148
+ except InferenceError as e:
149
+ return f"**{e}**", format_code_html(code, detected_lang)
 
150
 
151
 
152
  # ---------------------------------------------------------------------------
hf_client.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Helpers for talking to the HuggingFace Inference API.
2
+
3
+ Centralises the three things every Space here needs and previously got wrong:
4
+
5
+ * **Authentication** -- reads ``HF_TOKEN`` from the environment. The serverless
6
+ Inference Providers backend rejects most anonymous calls (401/402), so a token
7
+ is effectively required. Add an ``HF_TOKEN`` secret in the Space settings.
8
+ * **Timeouts** -- a hung provider request no longer blocks the worker forever.
9
+ * **Retries** -- transient errors (model cold-start 503s, rate limits) are
10
+ retried with exponential back-off instead of failing on the first hit.
11
+
12
+ The ``huggingface_hub`` import is deferred into :func:`make_client` so the pure
13
+ helpers (:func:`friendly_error`, :func:`with_retry`) can be imported and unit
14
+ tested without the dependency installed.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ import os
21
+ import time
22
+ from typing import Callable, Optional, TypeVar
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ DEFAULT_TIMEOUT = 120 # seconds
27
+ MAX_RETRIES = 3
28
+ BACKOFF_BASE = 2.0 # seconds
29
+
30
+ T = TypeVar("T")
31
+
32
+ _TRANSIENT_MARKERS = (
33
+ "loading",
34
+ "currently loading",
35
+ "starting",
36
+ "503",
37
+ "rate limit",
38
+ "429",
39
+ "too many requests",
40
+ "quota",
41
+ "timeout",
42
+ "timed out",
43
+ )
44
+
45
+
46
+ class InferenceError(RuntimeError):
47
+ """Raised when an inference call fails after exhausting retries."""
48
+
49
+
50
+ def get_token() -> Optional[str]:
51
+ """Return the HF token from the environment, if configured."""
52
+ return os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
53
+
54
+
55
+ def make_client(model: Optional[str] = None, timeout: int = DEFAULT_TIMEOUT):
56
+ """Create an :class:`InferenceClient` authenticated with the Space token."""
57
+ from huggingface_hub import InferenceClient # deferred: keeps helpers dep-free
58
+
59
+ token = get_token()
60
+ if token is None:
61
+ logger.warning(
62
+ "No HF_TOKEN found in the environment; inference calls will likely be "
63
+ "rejected. Add an HF_TOKEN secret in the Space settings."
64
+ )
65
+ return InferenceClient(model=model, token=token, timeout=timeout)
66
+
67
+
68
+ def _is_transient(exc: Exception) -> bool:
69
+ msg = str(exc).lower()
70
+ return any(marker in msg for marker in _TRANSIENT_MARKERS)
71
+
72
+
73
+ def friendly_error(exc: Exception) -> str:
74
+ """Map a raw inference exception to an actionable, user-facing message."""
75
+ msg = str(exc).lower()
76
+ if any(k in msg for k in ("rate limit", "429", "too many requests", "quota")):
77
+ return (
78
+ "The model is rate-limited right now. Wait a moment and try again, or "
79
+ "use an HF_TOKEN that has inference credits."
80
+ )
81
+ if any(k in msg for k in ("currently loading", "loading", "503", "starting")):
82
+ return "The model is warming up (cold start). Please try again in ~20 seconds."
83
+ if any(k in msg for k in ("401", "unauthorized", "402", "authentication")):
84
+ return (
85
+ "Inference was rejected for authentication. Set a valid HF_TOKEN secret "
86
+ "in the Space settings."
87
+ )
88
+ if "timeout" in msg or "timed out" in msg:
89
+ return "The request timed out -- the model may be busy. Please try again."
90
+ return f"Inference failed: {exc}"
91
+
92
+
93
+ def with_retry(
94
+ fn: Callable[..., T],
95
+ *args,
96
+ retries: int = MAX_RETRIES,
97
+ sleep: Callable[[float], None] = time.sleep,
98
+ **kwargs,
99
+ ) -> T:
100
+ """Call *fn* with exponential back-off on transient errors.
101
+
102
+ Raises :class:`InferenceError` with a friendly message if all attempts fail.
103
+ """
104
+ last_exc: Optional[Exception] = None
105
+ for attempt in range(retries):
106
+ try:
107
+ return fn(*args, **kwargs)
108
+ except Exception as exc: # noqa: BLE001 - re-raised via InferenceError
109
+ last_exc = exc
110
+ if not _is_transient(exc) or attempt == retries - 1:
111
+ break
112
+ wait = BACKOFF_BASE * (2**attempt)
113
+ logger.info(
114
+ "Transient inference error (attempt %d/%d); retrying in %.1fs: %s",
115
+ attempt + 1,
116
+ retries,
117
+ wait,
118
+ exc,
119
+ )
120
+ sleep(wait)
121
+ raise InferenceError(friendly_error(last_exc)) from last_exc
requirements.txt CHANGED
@@ -1,3 +1,3 @@
1
- gradio>=5.31.0
2
- huggingface_hub>=0.25.0
3
- pygments>=2.17.0
 
1
+ gradio>=5.31.0,<6.0.0
2
+ huggingface_hub>=0.25.0,<1.0.0
3
+ pygments>=2.17.0,<3.0.0