File size: 5,257 Bytes
2f00f43
 
af6cd33
236d464
 
 
 
 
2f00f43
af6cd33
236d464
 
 
af6cd33
 
 
236d464
af6cd33
 
 
2f00f43
 
af6cd33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6b3f603
 
 
 
 
 
 
af6cd33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f00f43
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import axios from "axios";

// ── Backend configuration ────────────────────────────────────────────────────
// If running in the browser, we default to the current origin
const CURRENT_ORIGIN = typeof window !== "undefined" ? window.location.origin : "";

// Option A: Traditional FastAPI backend
const BACKEND_URL = process.env.REACT_APP_BACKEND_URL || CURRENT_ORIGIN;

// Option B: Hugging Face Spaces Gradio backend
// If we are on a .hf.space domain, we default to using Gradio
const isHFSpace = typeof window !== "undefined" && window.location.hostname.endsWith(".hf.space");
const HF_SPACE_URL = process.env.REACT_APP_HF_SPACE_URL || (isHFSpace ? CURRENT_ORIGIN : "");

// When HF_SPACE_URL is set, the frontend routes all calls through Gradio's
// /api/<fn_name> REST endpoints instead of the FastAPI /api/* routes.
const useGradio = !!HF_SPACE_URL && isHFSpace;

// ── Axios instance for FastAPI mode ──────────────────────────────────────────
export const API = `${BACKEND_URL}/api`;
export const api = axios.create({ baseURL: API, timeout: 180000 });

// ── Gradio API caller ────────────────────────────────────────────────────────
// Gradio exposes each function as a POST endpoint at /api/<api_name>
// Request body: { data: [...args] }
// Response body: { data: [...outputs] }
async function gradioCall(fnName, ...args) {
  const url = `${HF_SPACE_URL}/api/${fnName}`;
  const resp = await axios.post(url, { data: args }, { timeout: 180000 });
  // Gradio returns { data: [output1, output2, ...] }
  // Our functions return a single JSON string β†’ parse it
  const raw = resp.data?.data?.[0];
  if (typeof raw === "string") {
    try {
      return JSON.parse(raw);
    } catch {
      return raw;
    }
  }
  return raw;
}

// ── Unified API adapter ─────────────────────────────────────────────────────
// Drop-in replacement: every page keeps calling `forgesight.getMetrics()` etc.
// Under the hood it routes to either FastAPI or Gradio.

export const forgesight = {
  // GET /api/ β†’ health
  async health() {
    if (useGradio) return gradioCall("health");
    const { data } = await api.get("/");
    return data;
  },

  // POST /api/inspections
  async createInspection({ image_base64, notes, product_spec, source }) {
    if (useGradio) {
      return gradioCall("inspect", image_base64, notes || "", product_spec || "", source || "upload");
    }
    const { data } = await api.post("/inspections", { image_base64, notes, product_spec, source });
    return data;
  },

  // GET /api/inspections
  async listInspections(limit = 50) {
    if (useGradio) return gradioCall("list_inspections", limit);
    const { data } = await api.get("/inspections", { params: { limit } });
    return data;
  },

  // GET /api/inspections/:id
  async getInspection(id) {
    if (useGradio) return gradioCall("get_inspection", id);
    const { data } = await api.get(`/inspections/${id}`);
    return data;
  },

  // GET /api/metrics
  async getMetrics() {
    if (useGradio) return gradioCall("metrics");
    const { data } = await api.get("/metrics");
    return data;
  },

  // GET /api/telemetry
  async getTelemetry() {
    if (useGradio) return gradioCall("telemetry");
    const { data } = await api.get("/telemetry");
    return data;
  },

  // GET /api/blueprint
  async getBlueprint() {
    if (useGradio) return gradioCall("blueprint");
    const { data } = await api.get("/blueprint");
    return data;
  },

  // GET /api/journal
  async listJournal() {
    if (useGradio) return gradioCall("journal_list");
    const { data } = await api.get("/journal");
    return data;
  },

  // POST /api/journal
  async createJournal({ title, body, tags }) {
    if (useGradio) {
      // Gradio version takes tags as comma-separated string
      const tagsStr = Array.isArray(tags) ? tags.join(", ") : tags || "";
      return gradioCall("journal_create", title, body, tagsStr);
    }
    const { data } = await api.post("/journal", { title, body, tags });
    return data;
  },

  // POST /api/journal/seed
  async seedJournal() {
    if (useGradio) {
      // Gradio auto-seeds on journal_list; no-op here
      return { seeded: 0, reason: "auto-seeded via journal_list" };
    }
    const { data } = await api.post("/journal/seed");
    return data;
  },
};

// ── Utility ─────────────────────────────────────────────────────────────────
export const fileToBase64 = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const str = reader.result;
      const comma = str.indexOf(",");
      resolve(comma >= 0 ? str.slice(comma + 1) : str);
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });