Skip to content

Commit 66484ff

Browse files
rajbosCopilot
andcommitted
feat: add AI fluency score to sharing server dashboard
- Upload fluencyMetrics alongside token rollup data from extension - Store fluency_json column in server DB with auto-migration - Validate optional fluencyMetrics field in upload API - Aggregate per-upload fluency metrics across all rows - Port 6-category scoring logic (Prompt/Context/Agentic/Tool/Customization/Workflow) - Display fluency badge in dashboard header with overall stage + stars - Add clickable modal with radar chart (Chart.js) and per-category cards with tips - No new user settings required Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3a8a3fc commit 66484ff

5 files changed

Lines changed: 541 additions & 5 deletions

File tree

sharing-server/src/db.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface UploadRow {
2929
interactions: number;
3030
schema_version: number;
3131
uploaded_at: string;
32+
fluency_json: string | null;
3233
}
3334

3435
export interface UploadEntry {
@@ -43,6 +44,7 @@ export interface UploadEntry {
4344
inputTokens: number;
4445
outputTokens: number;
4546
interactions: number;
47+
fluencyMetrics?: Record<string, unknown>;
4648
}
4749

4850
let _db: DatabaseSync | undefined;
@@ -79,6 +81,7 @@ const UPLOADS_TABLE_DDL = `
7981
interactions INTEGER NOT NULL DEFAULT 0,
8082
schema_version INTEGER NOT NULL DEFAULT 3,
8183
uploaded_at TEXT DEFAULT (datetime('now')),
84+
fluency_json TEXT,
8285
UNIQUE(user_id, dataset_id, day, model, workspace_id, machine_id, editor)
8386
)`;
8487

@@ -147,6 +150,14 @@ function initSchema(db: DatabaseSync): void {
147150
CREATE INDEX IF NOT EXISTS idx_uploads_user_day ON usage_uploads(user_id, day);
148151
CREATE INDEX IF NOT EXISTS idx_uploads_dataset ON usage_uploads(dataset_id, day);
149152
`);
153+
154+
// Add fluency_json column if it doesn't exist (migration for existing DBs)
155+
const cols = db
156+
.prepare("PRAGMA table_info(usage_uploads)")
157+
.all() as unknown as Array<{ name: string }>;
158+
if (!cols.some(c => c.name === 'fluency_json')) {
159+
db.exec('ALTER TABLE usage_uploads ADD COLUMN fluency_json TEXT');
160+
}
150161
}
151162

152163
export function upsertUser(
@@ -178,17 +189,19 @@ export function getUserByGithubId(githubId: number): UserRow | undefined {
178189

179190
export function upsertUpload(userId: number, entry: UploadEntry): void {
180191
const editor = ((entry.editor ?? '').trim() || 'VS Code').slice(0, 100);
192+
const fluencyJson = entry.fluencyMetrics ? JSON.stringify(entry.fluencyMetrics) : null;
181193
getDb().prepare(`
182194
INSERT INTO usage_uploads
183195
(user_id, dataset_id, day, model, workspace_id, workspace_name, machine_id, machine_name,
184-
editor, input_tokens, output_tokens, interactions)
185-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
196+
editor, input_tokens, output_tokens, interactions, fluency_json)
197+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
186198
ON CONFLICT(user_id, dataset_id, day, model, workspace_id, machine_id, editor) DO UPDATE SET
187199
workspace_name = excluded.workspace_name,
188200
machine_name = excluded.machine_name,
189201
input_tokens = excluded.input_tokens,
190202
output_tokens = excluded.output_tokens,
191203
interactions = excluded.interactions,
204+
fluency_json = excluded.fluency_json,
192205
uploaded_at = datetime('now')
193206
`).run(
194207
userId,
@@ -203,6 +216,7 @@ export function upsertUpload(userId: number, entry: UploadEntry): void {
203216
entry.inputTokens,
204217
entry.outputTokens,
205218
entry.interactions,
219+
fluencyJson,
206220
);
207221
}
208222

sharing-server/src/routes/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ function validateEntry(entry: unknown): string | null {
175175
return `"editor" too long (max ${MAX_STRING_LENGTHS.editor})`;
176176
}
177177
}
178+
if (e.fluencyMetrics !== undefined && e.fluencyMetrics !== null) {
179+
if (typeof e.fluencyMetrics !== 'object' || Array.isArray(e.fluencyMetrics)) {
180+
return '"fluencyMetrics" must be an object';
181+
}
182+
}
178183

179184
return null;
180185
}

0 commit comments

Comments
 (0)