chore(ai): return AI node/GPU facts and copy public endpoint

This commit is contained in:
Brad Stein 2025-12-21 00:30:55 -03:00
parent 3ce4ac3273
commit eeaece5bae
2 changed files with 11 additions and 11 deletions

View File

@ -34,11 +34,9 @@ AI_CHAT_SYSTEM_PROMPT = os.getenv(
"You are the Titan Lab assistant for bstein.dev. Be concise and helpful.", "You are the Titan Lab assistant for bstein.dev. Be concise and helpful.",
) )
AI_CHAT_TIMEOUT_SEC = float(os.getenv("AI_CHAT_TIMEOUT_SEC", "20")) AI_CHAT_TIMEOUT_SEC = float(os.getenv("AI_CHAT_TIMEOUT_SEC", "20"))
AI_NODE_NAME = os.getenv("AI_NODE_NAME") or os.getenv("NODE_NAME") or "unknown" AI_NODE_NAME = os.getenv("AI_CHAT_NODE_NAME") or os.getenv("AI_NODE_NAME") or "ai-cluster"
try: AI_GPU_DESC = os.getenv("AI_CHAT_GPU_DESC") or "local GPU (dynamic)"
AI_NODE_GPU_MAP = json.loads(os.getenv("AI_NODE_GPU_MAP", "{}")) AI_PUBLIC_ENDPOINT = os.getenv("AI_PUBLIC_CHAT_ENDPOINT", "https://chat.ai.bstein.dev/api/ai/chat")
except json.JSONDecodeError:
AI_NODE_GPU_MAP = {}
_LAB_STATUS_CACHE: dict[str, Any] = {"ts": 0.0, "value": None} _LAB_STATUS_CACHE: dict[str, Any] = {"ts": 0.0, "value": None}
@ -188,13 +186,12 @@ def ai_chat() -> Any:
@app.route("/api/ai/info", methods=["GET"]) @app.route("/api/ai/info", methods=["GET"])
def ai_info() -> Any: def ai_info() -> Any:
gpu_label = AI_NODE_GPU_MAP.get(AI_NODE_NAME, "local GPU (dynamic)")
return jsonify( return jsonify(
{ {
"node": AI_NODE_NAME, "node": AI_NODE_NAME,
"gpu": gpu_label, "gpu": AI_GPU_DESC,
"model": AI_CHAT_MODEL, "model": AI_CHAT_MODEL,
"endpoint": "/api/ai/chat", "endpoint": AI_PUBLIC_ENDPOINT or "/api/ai/chat",
} }
) )

View File

@ -20,13 +20,13 @@
<span class="value mono">{{ meta.gpu }}</span> <span class="value mono">{{ meta.gpu }}</span>
</div> </div>
<div class="fact"> <div class="fact">
<span class="label mono">Node</span> <span class="label mono">AI Node</span>
<span class="value mono">{{ meta.node }}</span> <span class="value mono">{{ meta.node }}</span>
</div> </div>
<div class="fact"> <div class="fact">
<span class="label mono">Endpoint</span> <span class="label mono">Endpoint</span>
<button class="endpoint-copy mono" type="button" @click="copyCurl"> <button class="endpoint-copy mono" type="button" @click="copyCurl">
{{ apiHost }} {{ meta.endpoint || apiHost }}
<span v-if="copied" class="copied">copied</span> <span v-if="copied" class="copied">copied</span>
</button> </button>
</div> </div>
@ -98,6 +98,7 @@ const meta = ref({
model: "loading...", model: "loading...",
gpu: "local GPU (dynamic)", gpu: "local GPU (dynamic)",
node: "unknown", node: "unknown",
endpoint: "",
}); });
const messages = ref([ const messages = ref([
{ {
@ -128,6 +129,7 @@ async function fetchMeta() {
model: data.model || meta.value.model, model: data.model || meta.value.model,
gpu: data.gpu || meta.value.gpu, gpu: data.gpu || meta.value.gpu,
node: data.node || meta.value.node, node: data.node || meta.value.node,
endpoint: data.endpoint || meta.value.endpoint || apiHost,
}; };
} catch { } catch {
// swallow // swallow
@ -211,7 +213,8 @@ function handleKeydown(e) {
} }
async function copyCurl() { async function copyCurl() {
const curl = `curl -X POST ${new URL(API_URL, window.location.href).toString()} -H 'content-type: application/json' -d '{\"message\":\"hi\"}'`; const target = meta.value.endpoint || new URL(API_URL, window.location.href).toString();
const curl = `curl -X POST ${target} -H 'content-type: application/json' -d '{\"message\":\"hi\"}'`;
try { try {
await navigator.clipboard.writeText(curl); await navigator.clipboard.writeText(curl);
copied.value = true; copied.value = true;