[eric] [eric] add PostHog analytics, 9Router subscription proxy, unified usage tracking

- PostHog integration: collector, analytics subapp, opt-in UI, Analytics page
  - 9Router: auto-start, OAuth subscription flow, /v1/messages Anthropic format support
  - Settings overhaul: multi-provider API keys, subscription connect UI, onboarding modal
  - Unified usage: merge 9Router cost/token data into Settings Usage tab
  - Provider system: providers/, agent_loop, tools/ (unused, for future non-Anthropic support)
  - Agent SDK: restored as primary with 9Router ANTHROPIC_BASE_URL fallback
  - Updated system prompt, credential resolution, dashboard analytics
This commit is contained in:
ciregenz
2026-03-24 14:02:58 -07:00
parent 8d09e46df5
commit ebea25f0c2
38 changed files with 4382 additions and 373 deletions
+49
View File
@@ -0,0 +1,49 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { API_BASE } from '@/shared/config';
const AGENTS_API = `${API_BASE}/agents`;
export interface ModelOption {
value: string;
label: string;
version?: string;
context_window: number;
}
interface ModelsState {
byProvider: Record<string, ModelOption[]>;
loaded: boolean;
}
const initialState: ModelsState = {
byProvider: {},
loaded: false,
};
export const fetchModels = createAsyncThunk('models/fetchModels', async () => {
const res = await fetch(`${AGENTS_API}/models`);
if (!res.ok) throw new Error('Failed to fetch models');
const data = await res.json();
// API returns { models: { provider: [...] } }
const models = data.models || data;
return models as Record<string, ModelOption[]>;
});
const modelsSlice = createSlice({
name: 'models',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchModels.fulfilled, (state, action) => {
state.byProvider = action.payload;
state.loaded = true;
})
.addCase(fetchModels.rejected, (state) => {
// Mark as loaded even on failure so we fall back to hardcoded options
state.loaded = true;
});
},
});
export default modelsSlice.reducer;