Bot commited on
Commit
ca02f17
·
1 Parent(s): b973b15

feat: implement dynamic recommended model selection in the Eternity Workstation dashboard

Browse files
src/app/api/models-status/route.ts ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { getSession } from "auth/server";
3
+
4
+ const getBackendUrl = () => {
5
+ if (process.env.BACKEND_API_URL) return process.env.BACKEND_API_URL;
6
+ const spaceId = process.env.SPACE_ID || "";
7
+ if (spaceId.startsWith("shyota/")) {
8
+ return "https://shyota-claude-code-backend.hf.space";
9
+ }
10
+ return "https://augment17-claude-code-backend.hf.space";
11
+ };
12
+
13
+ export async function GET(req: NextRequest) {
14
+ const session = await getSession();
15
+ if (!session?.user) {
16
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
17
+ }
18
+
19
+ const backendUrl = getBackendUrl();
20
+ const apiKey = process.env.BACKEND_API_KEY || "";
21
+
22
+ try {
23
+ const res = await fetch(`${backendUrl}/api/models-status`, {
24
+ headers: {
25
+ "Authorization": `Bearer ${apiKey}`,
26
+ },
27
+ cache: "no-store",
28
+ });
29
+
30
+ if (!res.ok) {
31
+ const txt = await res.text();
32
+ return NextResponse.json({ error: txt }, { status: res.status });
33
+ }
34
+
35
+ const data = await res.json();
36
+ return NextResponse.json(data);
37
+ } catch (err: any) {
38
+ return NextResponse.json({ error: err.message }, { status: 500 });
39
+ }
40
+ }
src/app/api/settings/route.ts ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { getSession } from "auth/server";
3
+
4
+ const getBackendUrl = () => {
5
+ if (process.env.BACKEND_API_URL) return process.env.BACKEND_API_URL;
6
+ const spaceId = process.env.SPACE_ID || "";
7
+ if (spaceId.startsWith("shyota/")) {
8
+ return "https://shyota-claude-code-backend.hf.space";
9
+ }
10
+ return "https://augment17-claude-code-backend.hf.space";
11
+ };
12
+
13
+ export async function POST(req: NextRequest) {
14
+ const session = await getSession();
15
+ if (!session?.user) {
16
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
17
+ }
18
+
19
+ const backendUrl = getBackendUrl();
20
+ const apiKey = process.env.BACKEND_API_KEY || "";
21
+
22
+ try {
23
+ const body = await req.json();
24
+ const res = await fetch(`${backendUrl}/api/settings/model`, {
25
+ method: "POST",
26
+ headers: {
27
+ "Content-Type": "application/json",
28
+ "Authorization": `Bearer ${apiKey}`,
29
+ },
30
+ body: JSON.stringify(body),
31
+ });
32
+
33
+ if (!res.ok) {
34
+ const txt = await res.text();
35
+ return NextResponse.json({ error: txt }, { status: res.status });
36
+ }
37
+
38
+ const data = await res.json();
39
+ return NextResponse.json(data);
40
+ } catch (err: any) {
41
+ return NextResponse.json({ error: err.message }, { status: 400 });
42
+ }
43
+ }
src/components/agent-monitor.tsx CHANGED
@@ -44,6 +44,8 @@ export default function AgentMonitorPanel({ threadId }: { threadId: string }) {
44
  // Eternity states
45
  const [projects, setProjects] = useState<EternityProject[]>([]);
46
  const [isEternityLoading, setIsEternityLoading] = useState(false);
 
 
47
 
48
  // Logs states
49
  const [logs, setLogs] = useState<string[]>([]);
@@ -77,7 +79,10 @@ export default function AgentMonitorPanel({ threadId }: { threadId: string }) {
77
  }, 3000);
78
 
79
  // Initial calls
80
- if (activeTab === "eternity") fetchProjects();
 
 
 
81
  if (activeTab === "logs") fetchLogs();
82
 
83
  return () => {
@@ -135,6 +140,38 @@ export default function AgentMonitorPanel({ threadId }: { threadId: string }) {
135
  }
136
  };
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  const fetchLogs = async () => {
139
  try {
140
  const res = await fetch("/api/logs");
@@ -338,6 +375,30 @@ export default function AgentMonitorPanel({ threadId }: { threadId: string }) {
338
  {activeTab === "eternity" && (
339
  <ScrollArea className="h-full w-full">
340
  <div className="p-4 space-y-4">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  <div className="text-[10px] uppercase font-bold text-muted-foreground/80 tracking-wider mb-1">
342
  ACTIVE MULTI-AGENT PROJECTS
343
  </div>
 
44
  // Eternity states
45
  const [projects, setProjects] = useState<EternityProject[]>([]);
46
  const [isEternityLoading, setIsEternityLoading] = useState(false);
47
+ const [recommendedModel, setRecommendedModel] = useState<string>("");
48
+ const [availableModels, setAvailableModels] = useState<{ id: string; name: string }[]>([]);
49
 
50
  // Logs states
51
  const [logs, setLogs] = useState<string[]>([]);
 
79
  }, 3000);
80
 
81
  // Initial calls
82
+ if (activeTab === "eternity") {
83
+ fetchProjects();
84
+ fetchModels();
85
+ }
86
  if (activeTab === "logs") fetchLogs();
87
 
88
  return () => {
 
140
  }
141
  };
142
 
143
+ const fetchModels = async () => {
144
+ try {
145
+ const res = await fetch("/api/models-status");
146
+ if (!res.ok) throw new Error("Failed to load models");
147
+ const data = await res.json();
148
+ const list = data.map((m: any) => ({
149
+ id: m.id,
150
+ name: m.name + (m.status.includes("ONLINE") ? "" : " (Offline)"),
151
+ }));
152
+ setAvailableModels(list);
153
+ const rec = data.find((m: any) => m.is_recommended);
154
+ if (rec) setRecommendedModel(rec.id);
155
+ } catch (err) {
156
+ console.error(err);
157
+ }
158
+ };
159
+
160
+ const handleSetRecommendedModel = async (modelId: string) => {
161
+ try {
162
+ const res = await fetch("/api/settings", {
163
+ method: "POST",
164
+ headers: { "Content-Type": "application/json" },
165
+ body: JSON.stringify({ model: modelId }),
166
+ });
167
+ if (!res.ok) throw new Error(await res.text());
168
+ setRecommendedModel(modelId);
169
+ toast.success(`Active recommended model set to ${modelId}`);
170
+ } catch (err: any) {
171
+ toast.error(`Failed to update model: ${err.message}`);
172
+ }
173
+ };
174
+
175
  const fetchLogs = async () => {
176
  try {
177
  const res = await fetch("/api/logs");
 
375
  {activeTab === "eternity" && (
376
  <ScrollArea className="h-full w-full">
377
  <div className="p-4 space-y-4">
378
+ {/* Model Selector Section */}
379
+ <div className="border border-border/60 rounded-xl p-3 bg-muted/10 space-y-2">
380
+ <div className="text-[10px] uppercase font-bold text-muted-foreground/80 tracking-wider">
381
+ Orchestration Agent Model
382
+ </div>
383
+ <div className="flex items-center gap-2">
384
+ <select
385
+ value={recommendedModel}
386
+ onChange={(e) => handleSetRecommendedModel(e.target.value)}
387
+ className="flex-1 bg-background border border-input rounded p-1.5 text-xs text-foreground focus:outline-none cursor-pointer"
388
+ >
389
+ {availableModels.length === 0 ? (
390
+ <option value="">Loading models...</option>
391
+ ) : (
392
+ availableModels.map((m) => (
393
+ <option key={m.id} value={m.id}>
394
+ {m.name}
395
+ </option>
396
+ ))
397
+ )}
398
+ </select>
399
+ </div>
400
+ </div>
401
+
402
  <div className="text-[10px] uppercase font-bold text-muted-foreground/80 tracking-wider mb-1">
403
  ACTIVE MULTI-AGENT PROJECTS
404
  </div>