SENODROOM commited on
Commit
beef333
·
1 Parent(s): aff9c7f
README.md CHANGED
@@ -4,6 +4,24 @@ FastAPI service that rates a public GitHub user profile out of 100.
4
 
5
  The API accepts a GitHub username and returns a `rating_score`, developer level, language breakdown, contribution signals, streak data, and model metadata.
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  ## Token Behavior
8
 
9
  `GITHUB_TOKEN` is optional.
@@ -34,6 +52,12 @@ pip install -r requirements.txt
34
  uvicorn app.main:app --host 0.0.0.0 --port 8000
35
  ```
36
 
 
 
 
 
 
 
37
  Request a rating:
38
 
39
  ```bash
@@ -48,7 +72,50 @@ curl -X POST http://localhost:8000/analyze \
48
  docker compose up --build -d
49
  ```
50
 
51
- The API will be available on `http://localhost:${APP_PORT:-8000}`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  ## Response Fields
54
 
 
4
 
5
  The API accepts a GitHub username and returns a `rating_score`, developer level, language breakdown, contribution signals, streak data, and model metadata.
6
 
7
+ ## Folder Structure
8
+
9
+ ```text
10
+ app/
11
+ main.py FastAPI app entrypoint and static frontend serving
12
+ api/ HTTP routes for health checks and profile analysis
13
+ schemas.py Request and response models
14
+ service.py Application service layer
15
+ ai/ Embedding and scoring logic
16
+ clients/ GitHub API clients and queries
17
+ core/ Settings and logging
18
+ graph/ Active analysis workflow
19
+ static/index.html Browser API console
20
+ backend/ Legacy LangGraph prototype pipeline
21
+ experiments/ Standalone prototype files
22
+ scripts/ Developer utilities and CLI helpers
23
+ ```
24
+
25
  ## Token Behavior
26
 
27
  `GITHUB_TOKEN` is optional.
 
52
  uvicorn app.main:app --host 0.0.0.0 --port 8000
53
  ```
54
 
55
+ Open the browser console UI:
56
+
57
+ ```text
58
+ http://localhost:8000/
59
+ ```
60
+
61
  Request a rating:
62
 
63
  ```bash
 
72
  docker compose up --build -d
73
  ```
74
 
75
+ The API and `index.html` console will be available on `http://localhost:${APP_PORT:-8000}`.
76
+
77
+ Useful deployment commands:
78
+
79
+ ```bash
80
+ docker compose ps
81
+ docker compose logs -f api
82
+ docker compose down
83
+ ```
84
+
85
+ For a cloud VM, install Docker, clone the repo, create `.env`, then run the same `docker compose up --build -d` command. If you deploy behind a domain or reverse proxy, forward public traffic to container port `8000`.
86
+
87
+ ## Use The API In `index.html`
88
+
89
+ The frontend at `app/static/index.html` calls the API with `fetch`:
90
+
91
+ ```html
92
+ <script>
93
+ async function analyze(username) {
94
+ const response = await fetch("/analyze", {
95
+ method: "POST",
96
+ headers: { "Content-Type": "application/json" },
97
+ body: JSON.stringify({ username })
98
+ });
99
+
100
+ if (!response.ok) {
101
+ const error = await response.json();
102
+ throw new Error(error.detail || "Analyze request failed");
103
+ }
104
+
105
+ return response.json();
106
+ }
107
+ </script>
108
+ ```
109
+
110
+ Use a full base URL only when the HTML is hosted somewhere else:
111
+
112
+ ```js
113
+ fetch("https://your-domain.com/analyze", {
114
+ method: "POST",
115
+ headers: { "Content-Type": "application/json" },
116
+ body: JSON.stringify({ username: "octocat" })
117
+ });
118
+ ```
119
 
120
  ## Response Fields
121
 
app/api/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """API route package."""
app/api/routes.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from fastapi import APIRouter, HTTPException
4
+
5
+ from app.clients.github_graphql import GitHubAPIError
6
+ from app.schemas import AnalyzeRequest, AnalyzeResponse
7
+ from app.service import AnalyzerService
8
+
9
+ router = APIRouter()
10
+ service: AnalyzerService | None = None
11
+
12
+
13
+ def get_service() -> AnalyzerService:
14
+ global service
15
+ if service is None:
16
+ service = AnalyzerService()
17
+ return service
18
+
19
+
20
+ @router.get("/health")
21
+ async def health() -> dict[str, str]:
22
+ return {"status": "ok"}
23
+
24
+
25
+ @router.post("/analyze", response_model=AnalyzeResponse)
26
+ async def analyze(payload: AnalyzeRequest) -> AnalyzeResponse:
27
+ try:
28
+ return await get_service().analyze(payload.username)
29
+ except GitHubAPIError as exc:
30
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
31
+ except Exception as exc:
32
+ raise HTTPException(status_code=500, detail=str(exc)) from exc
app/main.py CHANGED
@@ -1,36 +1,24 @@
1
  from __future__ import annotations
2
 
3
- from fastapi import FastAPI, HTTPException
4
 
5
- from app.clients.github_graphql import GitHubAPIError
 
 
 
 
6
  from app.core.config import settings
7
  from app.core.logging import setup_logging
8
- from app.schemas import AnalyzeRequest, AnalyzeResponse
9
- from app.service import AnalyzerService
10
 
11
  setup_logging()
12
 
13
  app = FastAPI(title=settings.app_name, version=settings.app_version)
14
- service: AnalyzerService | None = None
15
-
16
-
17
- def get_service() -> AnalyzerService:
18
- global service
19
- if service is None:
20
- service = AnalyzerService()
21
- return service
22
-
23
 
24
- @app.get("/health")
25
- async def health() -> dict[str, str]:
26
- return {"status": "ok"}
27
 
28
 
29
- @app.post("/analyze", response_model=AnalyzeResponse)
30
- async def analyze(payload: AnalyzeRequest) -> AnalyzeResponse:
31
- try:
32
- return await get_service().analyze(payload.username)
33
- except GitHubAPIError as exc:
34
- raise HTTPException(status_code=400, detail=str(exc)) from exc
35
- except Exception as exc:
36
- raise HTTPException(status_code=500, detail=str(exc)) from exc
 
1
  from __future__ import annotations
2
 
3
+ from pathlib import Path
4
 
5
+ from fastapi import FastAPI
6
+ from fastapi.responses import FileResponse
7
+ from fastapi.staticfiles import StaticFiles
8
+
9
+ from app.api.routes import router
10
  from app.core.config import settings
11
  from app.core.logging import setup_logging
 
 
12
 
13
  setup_logging()
14
 
15
  app = FastAPI(title=settings.app_name, version=settings.app_version)
16
+ STATIC_DIR = Path(__file__).resolve().parent / "static"
 
 
 
 
 
 
 
 
17
 
18
+ app.include_router(router)
19
+ app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
 
20
 
21
 
22
+ @app.get("/")
23
+ async def index() -> FileResponse:
24
+ return FileResponse(STATIC_DIR / "index.html")
 
 
 
 
 
app/static/index.html ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GitHub Profile AI Reviewer</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: dark;
10
+ --bg: #0b0f13;
11
+ --panel: #121820;
12
+ --panel-soft: #18212b;
13
+ --line: #263241;
14
+ --text: #eef3f8;
15
+ --muted: #93a4b7;
16
+ --accent: #3fbf8f;
17
+ --accent-strong: #54d6a5;
18
+ --warn: #f7c56b;
19
+ --danger: #ff7b7b;
20
+ }
21
+
22
+ * {
23
+ box-sizing: border-box;
24
+ }
25
+
26
+ body {
27
+ margin: 0;
28
+ min-height: 100vh;
29
+ background: var(--bg);
30
+ color: var(--text);
31
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
32
+ }
33
+
34
+ header {
35
+ border-bottom: 1px solid var(--line);
36
+ background: rgba(11, 15, 19, 0.94);
37
+ position: sticky;
38
+ top: 0;
39
+ z-index: 10;
40
+ backdrop-filter: blur(12px);
41
+ }
42
+
43
+ .nav {
44
+ align-items: center;
45
+ display: flex;
46
+ gap: 18px;
47
+ justify-content: space-between;
48
+ margin: 0 auto;
49
+ max-width: 1120px;
50
+ padding: 16px 20px;
51
+ }
52
+
53
+ .brand {
54
+ align-items: center;
55
+ display: flex;
56
+ gap: 10px;
57
+ font-weight: 700;
58
+ }
59
+
60
+ .mark {
61
+ align-items: center;
62
+ aspect-ratio: 1;
63
+ background: var(--accent);
64
+ border-radius: 6px;
65
+ color: #06110d;
66
+ display: inline-flex;
67
+ font-weight: 900;
68
+ justify-content: center;
69
+ width: 32px;
70
+ }
71
+
72
+ .pill {
73
+ border: 1px solid var(--line);
74
+ border-radius: 999px;
75
+ color: var(--muted);
76
+ font-size: 13px;
77
+ padding: 7px 12px;
78
+ white-space: nowrap;
79
+ }
80
+
81
+ main {
82
+ margin: 0 auto;
83
+ max-width: 1120px;
84
+ padding: 32px 20px 56px;
85
+ }
86
+
87
+ .workspace {
88
+ display: grid;
89
+ gap: 20px;
90
+ grid-template-columns: minmax(0, 1fr) 360px;
91
+ align-items: start;
92
+ }
93
+
94
+ .hero {
95
+ padding: 14px 0 22px;
96
+ }
97
+
98
+ h1 {
99
+ font-size: clamp(34px, 6vw, 62px);
100
+ letter-spacing: 0;
101
+ line-height: 1.02;
102
+ margin: 0 0 16px;
103
+ max-width: 780px;
104
+ }
105
+
106
+ .lead {
107
+ color: var(--muted);
108
+ font-size: 18px;
109
+ line-height: 1.6;
110
+ margin: 0;
111
+ max-width: 720px;
112
+ }
113
+
114
+ .panel {
115
+ background: var(--panel);
116
+ border: 1px solid var(--line);
117
+ border-radius: 8px;
118
+ padding: 18px;
119
+ }
120
+
121
+ .analyzer {
122
+ display: grid;
123
+ gap: 16px;
124
+ }
125
+
126
+ label {
127
+ color: var(--muted);
128
+ display: block;
129
+ font-size: 13px;
130
+ font-weight: 700;
131
+ margin-bottom: 8px;
132
+ text-transform: uppercase;
133
+ }
134
+
135
+ .input-row {
136
+ display: grid;
137
+ gap: 10px;
138
+ grid-template-columns: minmax(0, 1fr) auto;
139
+ }
140
+
141
+ input {
142
+ background: #0e141a;
143
+ border: 1px solid var(--line);
144
+ border-radius: 6px;
145
+ color: var(--text);
146
+ font: inherit;
147
+ min-height: 46px;
148
+ outline: none;
149
+ padding: 0 13px;
150
+ width: 100%;
151
+ }
152
+
153
+ input:focus {
154
+ border-color: var(--accent);
155
+ box-shadow: 0 0 0 3px rgba(63, 191, 143, 0.16);
156
+ }
157
+
158
+ button {
159
+ background: var(--accent);
160
+ border: 0;
161
+ border-radius: 6px;
162
+ color: #05110d;
163
+ cursor: pointer;
164
+ font: inherit;
165
+ font-weight: 800;
166
+ min-height: 46px;
167
+ padding: 0 18px;
168
+ white-space: nowrap;
169
+ }
170
+
171
+ button:disabled {
172
+ cursor: wait;
173
+ opacity: 0.62;
174
+ }
175
+
176
+ .result-grid {
177
+ display: grid;
178
+ gap: 12px;
179
+ grid-template-columns: repeat(3, minmax(0, 1fr));
180
+ }
181
+
182
+ .metric {
183
+ background: var(--panel-soft);
184
+ border: 1px solid var(--line);
185
+ border-radius: 8px;
186
+ min-height: 92px;
187
+ padding: 14px;
188
+ }
189
+
190
+ .metric span {
191
+ color: var(--muted);
192
+ display: block;
193
+ font-size: 12px;
194
+ font-weight: 700;
195
+ text-transform: uppercase;
196
+ }
197
+
198
+ .metric strong {
199
+ display: block;
200
+ font-size: 30px;
201
+ line-height: 1;
202
+ margin-top: 10px;
203
+ overflow-wrap: anywhere;
204
+ }
205
+
206
+ .score strong {
207
+ color: var(--accent-strong);
208
+ font-size: 48px;
209
+ }
210
+
211
+ .status {
212
+ color: var(--muted);
213
+ min-height: 22px;
214
+ }
215
+
216
+ .error {
217
+ color: var(--danger);
218
+ }
219
+
220
+ .language-list {
221
+ display: flex;
222
+ flex-wrap: wrap;
223
+ gap: 8px;
224
+ margin-top: 4px;
225
+ }
226
+
227
+ .language-list span {
228
+ background: #0e141a;
229
+ border: 1px solid var(--line);
230
+ border-radius: 999px;
231
+ color: var(--muted);
232
+ font-size: 13px;
233
+ padding: 7px 10px;
234
+ }
235
+
236
+ pre {
237
+ background: #080c10;
238
+ border: 1px solid var(--line);
239
+ border-radius: 8px;
240
+ color: #b7c7d8;
241
+ font-size: 13px;
242
+ line-height: 1.55;
243
+ margin: 0;
244
+ overflow-x: auto;
245
+ padding: 14px;
246
+ }
247
+
248
+ .side {
249
+ display: grid;
250
+ gap: 20px;
251
+ }
252
+
253
+ .side h2,
254
+ .panel h2 {
255
+ font-size: 16px;
256
+ margin: 0 0 12px;
257
+ }
258
+
259
+ .api-base {
260
+ margin-bottom: 14px;
261
+ }
262
+
263
+ .small {
264
+ color: var(--muted);
265
+ font-size: 13px;
266
+ line-height: 1.5;
267
+ margin: 10px 0 0;
268
+ }
269
+
270
+ @media (max-width: 860px) {
271
+ .workspace {
272
+ grid-template-columns: 1fr;
273
+ }
274
+
275
+ .result-grid {
276
+ grid-template-columns: 1fr;
277
+ }
278
+ }
279
+
280
+ @media (max-width: 560px) {
281
+ .nav {
282
+ align-items: flex-start;
283
+ flex-direction: column;
284
+ }
285
+
286
+ .input-row {
287
+ grid-template-columns: 1fr;
288
+ }
289
+
290
+ button {
291
+ width: 100%;
292
+ }
293
+ }
294
+ </style>
295
+ </head>
296
+ <body>
297
+ <header>
298
+ <div class="nav">
299
+ <div class="brand"><span class="mark">G</span> GitHub Profile AI Reviewer</div>
300
+ <div class="pill" id="health-pill">API status: checking</div>
301
+ </div>
302
+ </header>
303
+
304
+ <main>
305
+ <section class="hero">
306
+ <h1>Rate any public GitHub profile out of 100.</h1>
307
+ <p class="lead">Submit a username and the API returns a developer rating, level, contribution signals, language mix, and scoring metadata. A server token is optional.</p>
308
+ </section>
309
+
310
+ <section class="workspace">
311
+ <div class="panel analyzer">
312
+ <form id="analyze-form">
313
+ <label for="username">GitHub username</label>
314
+ <div class="input-row">
315
+ <input id="username" name="username" value="octocat" autocomplete="off" maxlength="39" pattern="[A-Za-z0-9-]+" required>
316
+ <button id="submit-button" type="submit">Analyze</button>
317
+ </div>
318
+ </form>
319
+
320
+ <div class="status" id="status">Ready.</div>
321
+
322
+ <div class="result-grid">
323
+ <div class="metric score">
324
+ <span>Rating</span>
325
+ <strong id="rating">--</strong>
326
+ </div>
327
+ <div class="metric">
328
+ <span>Level</span>
329
+ <strong id="level">--</strong>
330
+ </div>
331
+ <div class="metric">
332
+ <span>Language</span>
333
+ <strong id="language">--</strong>
334
+ </div>
335
+ </div>
336
+
337
+ <div class="panel">
338
+ <h2>Language Breakdown</h2>
339
+ <div class="language-list" id="language-breakdown"></div>
340
+ </div>
341
+
342
+ <div class="panel">
343
+ <h2>Raw API Response</h2>
344
+ <pre id="response">{}</pre>
345
+ </div>
346
+ </div>
347
+
348
+ <aside class="side">
349
+ <div class="panel">
350
+ <h2>API Base</h2>
351
+ <input class="api-base" id="api-base" value="">
352
+ <p class="small">Leave this as the current origin when this page is served by FastAPI. Use `http://localhost:8000` if opening the HTML file directly.</p>
353
+ </div>
354
+
355
+ <div class="panel">
356
+ <h2>Fetch Example</h2>
357
+ <pre id="fetch-example"></pre>
358
+ </div>
359
+
360
+ <div class="panel">
361
+ <h2>Endpoint</h2>
362
+ <pre>POST /analyze
363
+ Content-Type: application/json
364
+
365
+ {
366
+ "username": "octocat"
367
+ }</pre>
368
+ </div>
369
+ </aside>
370
+ </section>
371
+ </main>
372
+
373
+ <script>
374
+ const form = document.getElementById("analyze-form");
375
+ const usernameInput = document.getElementById("username");
376
+ const apiBaseInput = document.getElementById("api-base");
377
+ const submitButton = document.getElementById("submit-button");
378
+ const statusText = document.getElementById("status");
379
+ const healthPill = document.getElementById("health-pill");
380
+ const responseBox = document.getElementById("response");
381
+ const fetchExample = document.getElementById("fetch-example");
382
+
383
+ const fields = {
384
+ rating: document.getElementById("rating"),
385
+ level: document.getElementById("level"),
386
+ language: document.getElementById("language"),
387
+ breakdown: document.getElementById("language-breakdown")
388
+ };
389
+
390
+ apiBaseInput.value = window.location.protocol === "file:" ? "http://localhost:8000" : window.location.origin;
391
+
392
+ function endpoint(path) {
393
+ return `${apiBaseInput.value.replace(/\/$/, "")}${path}`;
394
+ }
395
+
396
+ function setStatus(message, isError = false) {
397
+ statusText.textContent = message;
398
+ statusText.classList.toggle("error", isError);
399
+ }
400
+
401
+ function updateFetchExample() {
402
+ const username = usernameInput.value || "octocat";
403
+ fetchExample.textContent = `const response = await fetch("${endpoint("/analyze")}", {
404
+ method: "POST",
405
+ headers: { "Content-Type": "application/json" },
406
+ body: JSON.stringify({ username: "${username}" })
407
+ });
408
+
409
+ const profileRating = await response.json();`;
410
+ }
411
+
412
+ function renderResult(data) {
413
+ fields.rating.textContent = Number.isFinite(data.rating_score) ? `${data.rating_score}` : "--";
414
+ fields.level.textContent = data.developer_level || "--";
415
+ fields.language.textContent = data.strongest_language || "--";
416
+ fields.breakdown.innerHTML = "";
417
+
418
+ const entries = Object.entries(data.language_breakdown || {});
419
+ if (!entries.length) {
420
+ const empty = document.createElement("span");
421
+ empty.textContent = "No language data";
422
+ fields.breakdown.appendChild(empty);
423
+ }
424
+
425
+ for (const [language, percent] of entries) {
426
+ const item = document.createElement("span");
427
+ item.textContent = `${language}: ${percent}%`;
428
+ fields.breakdown.appendChild(item);
429
+ }
430
+
431
+ responseBox.textContent = JSON.stringify(data, null, 2);
432
+ }
433
+
434
+ async function checkHealth() {
435
+ try {
436
+ const response = await fetch(endpoint("/health"));
437
+ healthPill.textContent = response.ok ? "API status: online" : "API status: unavailable";
438
+ } catch {
439
+ healthPill.textContent = "API status: offline";
440
+ }
441
+ }
442
+
443
+ form.addEventListener("submit", async (event) => {
444
+ event.preventDefault();
445
+ const username = usernameInput.value.trim();
446
+ if (!username) {
447
+ setStatus("Enter a GitHub username.", true);
448
+ return;
449
+ }
450
+
451
+ submitButton.disabled = true;
452
+ setStatus(`Analyzing ${username}...`);
453
+ updateFetchExample();
454
+
455
+ try {
456
+ const response = await fetch(endpoint("/analyze"), {
457
+ method: "POST",
458
+ headers: { "Content-Type": "application/json" },
459
+ body: JSON.stringify({ username })
460
+ });
461
+
462
+ const data = await response.json();
463
+ if (!response.ok) {
464
+ throw new Error(data.detail || "Request failed.");
465
+ }
466
+
467
+ renderResult(data);
468
+ setStatus(`Finished ${username}.`);
469
+ } catch (error) {
470
+ setStatus(error.message, true);
471
+ } finally {
472
+ submitButton.disabled = false;
473
+ }
474
+ });
475
+
476
+ usernameInput.addEventListener("input", updateFetchExample);
477
+ apiBaseInput.addEventListener("input", () => {
478
+ updateFetchExample();
479
+ checkHealth();
480
+ });
481
+
482
+ updateFetchExample();
483
+ checkHealth();
484
+ </script>
485
+ </body>
486
+ </html>
prototype.py → experiments/prototype.py RENAMED
@@ -1,35 +1,35 @@
1
- import torch
2
- from transformers import AutoTokenizer, AutoModel
3
-
4
- print("Loading CodeBERT model... Please wait.")
5
-
6
- model_name = "microsoft/codebert-base"
7
- tokenizer = AutoTokenizer.from_pretrained(model_name)
8
- model = AutoModel.from_pretrained(model_name)
9
-
10
- print("Model loaded successfully!")
11
-
12
-
13
- sample_code = """
14
- def calculate_experience(repo_data):
15
- stars = repo_data.get('stars', 0)
16
- commits = repo_data.get('commits', 0)
17
- return (stars * 10) + commits
18
- """
19
-
20
- print(f"\nAnalyzing Sample Code Snippet:\n{sample_code}")
21
-
22
-
23
- inputs = tokenizer(sample_code, return_tensors="pt", padding=True, truncation=True)
24
-
25
- # 4. Generating Embeddings
26
- with torch.no_grad():
27
- outputs = model(**inputs)
28
-
29
- # 5. Extracting Vector (Last hidden state)
30
- embeddings = outputs.last_hidden_state
31
-
32
- print("\nAI Pipeline Success!")
33
- print(f"Vector Shape: {embeddings.shape}")
34
- print("Meaning: CodeBERT successfully converted your code into heavy AI numbers!")
35
-
 
1
+ import torch
2
+ from transformers import AutoTokenizer, AutoModel
3
+
4
+ print("Loading CodeBERT model... Please wait.")
5
+
6
+ model_name = "microsoft/codebert-base"
7
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
8
+ model = AutoModel.from_pretrained(model_name)
9
+
10
+ print("Model loaded successfully!")
11
+
12
+
13
+ sample_code = """
14
+ def calculate_experience(repo_data):
15
+ stars = repo_data.get('stars', 0)
16
+ commits = repo_data.get('commits', 0)
17
+ return (stars * 10) + commits
18
+ """
19
+
20
+ print(f"\nAnalyzing Sample Code Snippet:\n{sample_code}")
21
+
22
+
23
+ inputs = tokenizer(sample_code, return_tensors="pt", padding=True, truncation=True)
24
+
25
+ # 4. Generating Embeddings
26
+ with torch.no_grad():
27
+ outputs = model(**inputs)
28
+
29
+ # 5. Extracting Vector (Last hidden state)
30
+ embeddings = outputs.last_hidden_state
31
+
32
+ print("\nAI Pipeline Success!")
33
+ print(f"Vector Shape: {embeddings.shape}")
34
+ print("Meaning: CodeBERT successfully converted your code into heavy AI numbers!")
35
+
index.html DELETED
@@ -1,360 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>GitHub Profile AI-Reviewer Docs</title>
7
- <style>
8
- body {
9
- font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
10
- background-color: #0b0e14;
11
- color: #8b949e;
12
- margin: 0;
13
- padding: 0;
14
- overflow-x: hidden;
15
- }
16
-
17
- /* Top Navigation Bar */
18
- .navbar {
19
- display: flex;
20
- justify-content: space-between;
21
- align-items: center;
22
- padding: 20px 40px;
23
- background-color: #0b0e14;
24
- border-bottom: 1px solid #161b22;
25
- position: sticky;
26
- top: 0;
27
- z-index: 100;
28
- }
29
- .logo {
30
- font-weight: 700;
31
- color: #58a6ff;
32
- letter-spacing: 1px;
33
- font-size: 1.1rem;
34
- cursor: pointer;
35
- }
36
- .nav-links {
37
- display: flex;
38
- gap: 25px;
39
- }
40
- .nav-links a {
41
- color: #8b949e;
42
- text-decoration: none;
43
- font-size: 0.85rem;
44
- text-transform: uppercase;
45
- font-weight: 600;
46
- letter-spacing: 0.5px;
47
- padding: 6px 12px;
48
- border-radius: 4px;
49
- transition: all 0.2s ease;
50
- }
51
- .nav-links a:hover, .nav-links a.active {
52
- color: #f0f6fc;
53
- background-color: #21262d;
54
- }
55
- .status-badge {
56
- background-color: rgba(56, 139, 253, 0.1);
57
- color: #58a6ff;
58
- border: 1px solid rgba(56, 139, 253, 0.3);
59
- padding: 4px 12px;
60
- border-radius: 20px;
61
- font-size: 0.8rem;
62
- font-weight: bold;
63
- }
64
-
65
- /* Multi-Page Visibility Logic */
66
- .page {
67
- display: none; /* Sub pages hidden by default */
68
- animation: fadeIn 0.4s ease forwards;
69
- }
70
- .page.active-page {
71
- display: block; /* Only active page shows up */
72
- }
73
-
74
- @keyframes fadeIn {
75
- from { opacity: 0; transform: translateY(10px); }
76
- to { opacity: 1; transform: translateY(0); }
77
- }
78
-
79
- /* PAGE 1: HERO HOME STYLE */
80
- .hero {
81
- text-align: center;
82
- padding: 120px 20px 80px 20px;
83
- background: radial-gradient(circle at top, #161b22 0%, #0b0e14 70%);
84
- }
85
- .hero h1 {
86
- font-size: 4.5rem;
87
- font-weight: 900;
88
- color: #58a6ff;
89
- margin: 0;
90
- line-height: 1.1;
91
- letter-spacing: -1px;
92
- text-transform: uppercase;
93
- }
94
- .hero h1 span {
95
- color: #f0f6fc;
96
- display: block;
97
- }
98
- .hero p {
99
- max-width: 650px;
100
- margin: 30px auto;
101
- font-size: 1.15rem;
102
- color: #8b949e;
103
- line-height: 1.6;
104
- }
105
- .cta-group {
106
- display: flex;
107
- justify-content: center;
108
- gap: 15px;
109
- margin-top: 40px;
110
- }
111
- .btn-primary {
112
- background-color: #388bfd;
113
- color: #ffffff;
114
- padding: 14px 32px;
115
- border-radius: 6px;
116
- font-weight: 600;
117
- text-decoration: none;
118
- display: inline-flex;
119
- align-items: center;
120
- gap: 8px;
121
- box-shadow: 0 4px 14px rgba(56, 139, 253, 0.4);
122
- cursor: pointer;
123
- }
124
- .btn-secondary {
125
- background-color: transparent;
126
- color: #f0f6fc;
127
- border: 1px solid #30363d;
128
- padding: 14px 32px;
129
- border-radius: 6px;
130
- font-weight: 600;
131
- text-decoration: none;
132
- cursor: pointer;
133
- }
134
-
135
- /* Stats Bar */
136
- .stats-bar {
137
- border-top: 1px solid #161b22;
138
- border-bottom: 1px solid #161b22;
139
- padding: 50px 20px;
140
- background-color: #07090e;
141
- }
142
- .stats-grid {
143
- max-width: 1000px;
144
- margin: 0 auto;
145
- display: flex;
146
- justify-content: space-between;
147
- text-align: center;
148
- }
149
- .stat-item h3 {
150
- font-size: 2.5rem;
151
- color: #388bfd;
152
- margin: 0 0 5px 0;
153
- font-weight: 700;
154
- }
155
- .stat-item p {
156
- font-size: 0.75rem;
157
- text-transform: uppercase;
158
- letter-spacing: 1px;
159
- color: #8b949e;
160
- margin: 0;
161
- }
162
-
163
- /* PAGE 2 & PAGE 3: INNER PAGES STANDARD CONTENT */
164
- .content-area {
165
- max-width: 900px;
166
- margin: 60px auto;
167
- padding: 0 20px;
168
- }
169
- .content-area h2 {
170
- color: #f0f6fc;
171
- font-size: 2rem;
172
- border-bottom: 1px solid #30363d;
173
- padding-bottom: 12px;
174
- }
175
- .grid {
176
- display: grid;
177
- gap: 20px;
178
- margin-top: 30px;
179
- }
180
- .card {
181
- padding: 25px;
182
- background-color: #12161f;
183
- border-radius: 6px;
184
- border: 1px solid #21262d;
185
- }
186
- .card h4 {
187
- color: #58a6ff;
188
- margin: 0 0 10px 0;
189
- font-size: 1.2rem;
190
- }
191
- .card p {
192
- margin: 0;
193
- line-height: 1.5;
194
- }
195
- pre {
196
- background-color: #12161f;
197
- padding: 20px;
198
- border-radius: 6px;
199
- border: 1px solid #21262d;
200
- color: #ff7b72;
201
- overflow-x: auto;
202
- font-family: monospace;
203
- font-size: 0.95rem;
204
- }
205
- footer {
206
- text-align: center;
207
- margin-top: 100px;
208
- color: #8b949e;
209
- font-size: 0.85rem;
210
- border-top: 1px solid #161b22;
211
- padding: 30px 20px;
212
- }
213
- </style>
214
- </head>
215
- <body>
216
-
217
- <!-- Header Navigation Bar -->
218
- <div class="navbar">
219
- <div class="logo" onclick="navigateTo('home')">AI-REVIEWER v1.0</div>
220
- <div class="nav-links">
221
- <a href="#home" id="nav-home" class="active" onclick="navigateTo('home')">Home</a>
222
- <a href="#architecture" id="nav-architecture" onclick="navigateTo('architecture')">Architecture</a>
223
- <a href="#training-guide" id="nav-training-guide" onclick="navigateTo('training-guide')">Training Guide</a>
224
- </div>
225
- <div class="status-badge">● PIPELINE LIVE</div>
226
- </div>
227
-
228
-
229
- <!-- ================= PAGE 1: HOME PAGE ================= -->
230
- <div id="page-home" class="page active-page">
231
- <div class="hero">
232
- <h1>ANALYZE.<span>SCORE SMART.</span></h1>
233
- <p>The AI-Reviewer uses an orchestrated LangGraph agentic model framework to map public developer profiles, compute contextual CodeBERT embeddings, and evaluate comprehensive syntax engineering metrics seamlessly.</p>
234
-
235
- <div class="cta-group">
236
- <!-- Clicking these opens separate virtual inner pages! -->
237
- <button onclick="navigateTo('training-guide')" class="btn-primary">Open Analyzer →</button>
238
- <button onclick="navigateTo('architecture')" class="btn-secondary">How It Works</button>
239
- </div>
240
- </div>
241
-
242
- <div class="stats-bar">
243
- <div class="stats-grid">
244
- <div class="stat-item">
245
- <h3>5+</h3>
246
- <p>Execution Nodes</p>
247
- </div>
248
- <div class="stat-item">
249
- <h3>768</h3>
250
- <p>Vector Array Dims</p>
251
- </div>
252
- <div class="stat-item">
253
- <h3>100%</h3>
254
- <p>State Tracking</p>
255
- </div>
256
- <div class="stat-item">
257
- <h3>PyTorch</h3>
258
- <p>Fine-Tuning Core</p>
259
- </div>
260
- </div>
261
- </div>
262
- </div>
263
-
264
-
265
- <!-- ================= PAGE 2: ARCHITECTURE (HOW IT WORKS) ================= -->
266
- <div id="page-architecture" class="page">
267
- <div class="content-area">
268
- <h2>System Architecture Matrix</h2>
269
- <p>Our core model layers are segregated into specialized processing nodes orchestrated smoothly via a LangGraph state container loop:</p>
270
-
271
- <div class="grid">
272
- <div class="card">
273
- <h4>1. GitHub Metric Extractor Node</h4>
274
- <p>Triggers advanced GraphQL queries to pull authentic repository data, commitments, and language footprints securely from live server channels.</p>
275
- </div>
276
- <div class="card">
277
- <h4>2. CodeBERT Vectorizer Node</h4>
278
- <p>Utilizes HuggingFace CodeBERT transformers to map complex source code text sequences into high-density 768-dimensional AI vector spaces.</p>
279
- </div>
280
- <div class="card">
281
- <h4>3. PyTorch Fine-Tuning Scoring Node</h4>
282
- <p>Runs neural network backpropagation sequences to update layer configurations and calculate authentic capabilities metrics dynamically.</p>
283
- </div>
284
- <div class="card">
285
- <h4>4. StarCoder Quality Estimator Node</h4>
286
- <p>Evaluates syntactic deployment blocks, documentation maturity limits, and structural architecture patterns.</p>
287
- </div>
288
- <div class="card">
289
- <h4>5. Cosine Vector Similarity Search Node</h4>
290
- <p>Compares user embedding matrices against regional vector database spaces to output instant "Find developers like X" recomendation values.</p>
291
- </div>
292
- </div>
293
- </div>
294
- </div>
295
-
296
-
297
- <!-- ================= PAGE 3: TRAINING GUIDE (RUN TIME) ================= -->
298
- <div id="page-training-guide" class="page">
299
- <div class="content-area">
300
- <h2>Local Compilation & Model Status</h2>
301
- <p>Follow this deployment layout blueprint to execute and initialize the unified master orchestration pipeline layout on local development nodes:</p>
302
-
303
- <h3>Environmental Core Elements</h3>
304
- <pre>
305
- # Install required infrastructure packages
306
- pip install langgraph torch transformers python-dotenv</pre>
307
-
308
- <h3>Pipeline Launch Target Execution</h3>
309
- <pre>
310
- # Boot central pipeline execution flow matrix
311
- python backend/graph/pipeline.py</pre>
312
-
313
- <div class="card" style="margin-top: 30px; border-left: 4px solid #388bfd;">
314
- <h4 style="margin: 0 0 5px 0;">Model Execution Validation</h4>
315
- <p>Upon compilation, the terminal will dynamically display step-by-step neural loop weights optimization, structural fine-tuning epochs loss dropping to 0.0000, and comprehensive matrix comparison outputs automatically.</p>
316
- </div>
317
- </div>
318
- </div>
319
-
320
-
321
- <!-- Footer Component -->
322
- <footer>
323
- GitHub Profile AI-Reviewer Documentation • Presented via Adaptive Dynamic Engine
324
- </footer>
325
-
326
-
327
- <!-- Dynamic Multi-Page Router Script -->
328
- <script>
329
- function navigateTo(pageId) {
330
- // 1. Hide all virtual page layers
331
- const pages = document.querySelectorAll('.page');
332
- pages.forEach(page => page.classList.remove('active-page'));
333
-
334
- // 2. Remove active state colors from all menu items
335
- const navLinks = document.querySelectorAll('.nav-links a');
336
- navLinks.forEach(link => link.classList.remove('active'));
337
-
338
- // 3. Show the targeted page layer
339
- document.getElementById('page-' + pageId).classList.add('active-page');
340
-
341
- // 4. Highlight the matching navigation menu option
342
- const targetNav = document.getElementById('nav-' + pageId);
343
- if(targetNav) {
344
- targetNav.classList.add('active');
345
- }
346
-
347
- // 5. Smooth scroll window back to the top of the new page layout
348
- window.scrollTo({ top: 0, behavior: 'smooth' });
349
- }
350
-
351
- // Handle browser fallback entry checks based on URL hashes
352
- window.addEventListener('DOMContentLoaded', () => {
353
- const currentHash = window.location.hash.replace('#', '');
354
- if(['home', 'architecture', 'training-guide'].includes(currentHash)) {
355
- navigateTo(currentHash);
356
- }
357
- });
358
- </script>
359
- </body>
360
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main.py → scripts/analyze_user.py RENAMED
@@ -3,6 +3,12 @@ from __future__ import annotations
3
  import argparse
4
  import asyncio
5
  import json
 
 
 
 
 
 
6
 
7
  from app.service import AnalyzerService
8
 
 
3
  import argparse
4
  import asyncio
5
  import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ ROOT_DIR = Path(__file__).resolve().parents[1]
10
+ if str(ROOT_DIR) not in sys.path:
11
+ sys.path.insert(0, str(ROOT_DIR))
12
 
13
  from app.service import AnalyzerService
14
 
github_api.py → scripts/github_graphql_probe.py RENAMED
File without changes