Initial commit — AI-powered coding tutor (Professor)

Next.js 16, React 19, Monaco editor, Anthropic SDK, multi-provider AI,
Wandbox Python execution, iframe HTML preview, SQLite auth + session persistence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Aodhan Collins
2026-03-04 21:48:34 +00:00
commit f644937604
56 changed files with 14012 additions and 0 deletions

95
lib/providers.ts Normal file
View File

@@ -0,0 +1,95 @@
import type { ProviderId, ProviderConfig } from '@/types';
export interface ProviderDefinition {
id: ProviderId;
label: string;
requiresApiKey: boolean;
hasCustomBaseUrl: boolean;
defaultBaseUrl: string;
defaultModel: string;
modelSuggestions: string[];
}
export const PROVIDERS: ProviderDefinition[] = [
{
id: 'anthropic',
label: 'Anthropic (Claude)',
requiresApiKey: true,
hasCustomBaseUrl: false,
defaultBaseUrl: 'https://api.anthropic.com',
defaultModel: 'claude-sonnet-4-6',
modelSuggestions: [
'claude-sonnet-4-6',
'claude-haiku-4-5-20251001',
'claude-opus-4-6',
],
},
{
id: 'openrouter',
label: 'OpenRouter',
requiresApiKey: true,
hasCustomBaseUrl: false,
defaultBaseUrl: 'https://openrouter.ai/api/v1',
defaultModel: 'meta-llama/llama-3.1-8b-instruct:free',
modelSuggestions: [
'meta-llama/llama-3.1-8b-instruct:free',
'google/gemma-3-27b-it:free',
'mistralai/mistral-7b-instruct:free',
'deepseek/deepseek-chat-v3-0324:free',
'qwen/qwen3-8b:free',
],
},
{
id: 'lmstudio',
label: 'LM Studio',
requiresApiKey: false,
hasCustomBaseUrl: true,
defaultBaseUrl: 'http://localhost:1234/v1',
defaultModel: 'local-model',
modelSuggestions: [],
},
{
id: 'ollama',
label: 'Ollama',
requiresApiKey: false,
hasCustomBaseUrl: true,
defaultBaseUrl: 'http://localhost:11434/v1',
defaultModel: 'llama3.2',
modelSuggestions: [
'llama3.2',
'llama3.1',
'mistral',
'qwen2.5-coder',
'deepseek-coder-v2',
'phi4',
],
},
];
export const PROVIDER_MAP = Object.fromEntries(PROVIDERS.map((p) => [p.id, p])) as Record<
ProviderId,
ProviderDefinition
>;
export const DEFAULT_PROVIDER_CONFIG: ProviderConfig = {
provider: 'anthropic',
model: 'claude-sonnet-4-6',
};
const STORAGE_KEY = 'professor_provider_config';
export function loadProviderConfig(): ProviderConfig {
if (typeof window === 'undefined') return DEFAULT_PROVIDER_CONFIG;
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return DEFAULT_PROVIDER_CONFIG;
return JSON.parse(raw) as ProviderConfig;
} catch {
return DEFAULT_PROVIDER_CONFIG;
}
}
export function saveProviderConfig(config: ProviderConfig): void {
if (typeof window === 'undefined') return;
localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
}