File size: 3,084 Bytes
bcf46c3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { CreateMLCEngine } from "@mlc-ai/web-llm";

// The singleton instance of our local MLCEngine
let mlcEngine = null;

/**
 * Initializes the WebLLM engine with the specified model if not already initialized.
 * Allows tracking loading progress via the provided callback.
 */
export async function initLocalEngine(initProgressCallback) {
  if (mlcEngine) return mlcEngine;

  if (!navigator.gpu) {
    throw new Error("WEBGPU_UNSUPPORTED");
  }

  try {
    // We use a highly optimized, small Llama 3 model for browser efficiency
    const selectedModel = "Llama-3.1-8B-Instruct-q4f32_1-MLC-1k";
    mlcEngine = await CreateMLCEngine(selectedModel, {
      initProgressCallback: initProgressCallback,
    });
    return mlcEngine;
  } catch (err) {
    console.error("Failed to initialize local MLCEngine:", err);
    throw err;
  }
}

/**
 * Routes the inference request.
 * @param {string | object} payload - The prompt text or an object { prompt, image }
 * @param {boolean} isLocalModeEnabled - Whether local execution is toggled on
 * @param {string} gatewayUrl - URL of the OpticParse gateway
 * @param {string} apiKey - The user's API Key
 */
export async function routeInference(payload, isLocalModeEnabled, gatewayUrl, apiKey) {
  const isTextOnly = typeof payload === "string" || !payload.image;

  if (isTextOnly && isLocalModeEnabled) {
    // Condition A: Text-only and Local Mode is Enabled
    if (!mlcEngine) {
      throw new Error("Local model is not initialized. Please wait for it to load.");
    }
    
    const prompt = typeof payload === "string" ? payload : payload.prompt;
    
    // Execute entirely within the browser
    const response = await mlcEngine.chat.completions.create({
      messages: [{ role: "user", content: prompt }],
    });
    
    return {
      source: "local_webllm",
      data: response.choices[0].message.content,
    };
  } 
  
  if (isTextOnly && !isLocalModeEnabled) {
    // Condition B: Text-only but Local Mode is Disabled
    // In our original architecture, the frontend might not have a direct "text-parse" route
    // but if it did, we'd hit the gateway here. For simplicity if PhishVision or a standard 
    // text completion route exists on the gateway, we hit it. 
    // Let's assume we use a generic POST to a backend route, or we can just throw if 
    // we don't have a generic text endpoint in this boilerplate.
    throw new Error("Remote text parsing endpoint not yet implemented in frontend boilerplate.");
  }
  
  // Condition C: Vision / Image Parsing
  // Forward payload to the new Gateway endpoint for HuggingFace Processing
  const response = await fetch(`${gatewayUrl}/api/vision-parse`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": apiKey,
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Vision parse failed: ${response.status} - ${errorText}`);
  }

  const data = await response.json();
  return {
    source: "backend_hf",
    data: data,
  };
}