demeter
Autonomous Hydroponic Intelligencegit clone https://github.com/maydayv7/demeter
frontend/src/api/agentApi.js (4817B)
1import {
2 USE_MOCK_DATA,
3 MOCK_SEARCH_RESULT,
4 MOCK_DASHBOARD,
5} from "../data/mockData";
6
7const API_URL = process.env.REACT_APP_AGENT_API_URL || "http://localhost:8000";
8
9export const agentService = {
10 /**
11 * Uploads an image + sensors to create a new FMU (Functional Memory Unit)
12 */
13 async uploadFMU(file, sensors) {
14 if (USE_MOCK_DATA) {
15 await new Promise((r) => setTimeout(r, 500));
16 return { status: "success", fmu_id: "mock-fmu-ingest-001" };
17 }
18
19 const formData = new FormData();
20 formData.append("file", file);
21 formData.append(
22 "sensors",
23 JSON.stringify({
24 pH: parseFloat(sensors.pH),
25 EC: parseFloat(sensors.EC),
26 temp: parseFloat(sensors.temp),
27 humidity: parseFloat(sensors.humidity),
28 crop_id: sensors.crop_id || undefined,
29 }),
30 );
31 formData.append(
32 "metadata",
33 JSON.stringify({
34 crop: sensors.crop,
35 stage: sensors.stage,
36 crop_id: sensors.crop_id || undefined,
37 }),
38 );
39
40 const res = await fetch(`${API_URL}/ingest`, {
41 method: "POST",
42 body: formData,
43 });
44 if (!res.ok) throw new Error(res.statusText);
45 return res.json();
46 },
47
48 /**
49 * Searches for similar memories and gets an Agent Decision
50 */
51 async searchFMU(file, sensors) {
52 if (USE_MOCK_DATA) {
53 await new Promise((r) => setTimeout(r, 1200));
54 return MOCK_SEARCH_RESULT;
55 }
56
57 const formData = new FormData();
58 formData.append("file", file);
59 formData.append(
60 "sensors",
61 JSON.stringify({
62 pH: parseFloat(sensors.pH),
63 EC: parseFloat(sensors.EC),
64 temp: parseFloat(sensors.temp),
65 humidity: parseFloat(sensors.humidity),
66 crop: sensors.crop,
67 stage: sensors.stage,
68 crop_id: sensors.crop_id || undefined,
69 }),
70 );
71
72 const res = await fetch(`${API_URL}/search`, {
73 method: "POST",
74 body: formData,
75 });
76 if (!res.ok) throw new Error(res.statusText);
77 return res.json();
78 },
79
80 /**
81 * Translates a natural language query into a database filter using LLM
82 */
83 async queryText(text, cropId = null) {
84 if (USE_MOCK_DATA) {
85 await new Promise((r) => setTimeout(r, 600));
86 return {
87 status: "success",
88 results: MOCK_DASHBOARD.slice(0, 3).map((d, i) => ({
89 id: d.id,
90 score: 0.95 - i * 0.08,
91 payload: d.payload,
92 })),
93 query_logic: {
94 must: [
95 { key: "crop", match: "Tomato" },
96 { key: "outcome", match: "Positive" },
97 ],
98 },
99 };
100 }
101
102 const formData = new FormData();
103 formData.append("query", text);
104 if (cropId) formData.append("crop_id", cropId);
105
106 const res = await fetch(`${API_URL}/query-text`, {
107 method: "POST",
108 body: formData,
109 });
110 if (!res.ok) throw new Error(res.statusText);
111 return res.json();
112 },
113
114 /**
115 * Finds cosine-similar crops via Qdrant vector search
116 */
117 async querySimilarCrops(cropId, cropName, payload) {
118 if (USE_MOCK_DATA) {
119 await new Promise((r) => setTimeout(r, 400));
120 return {
121 status: "success",
122 results: MOCK_DASHBOARD.slice(1, 4).map((d, i) => ({
123 id: d.id,
124 score: 0.91 - i * 0.07,
125 payload: d.payload,
126 })),
127 };
128 }
129
130 const formData = new FormData();
131 formData.append("crop_id", cropId);
132 formData.append("crop_name", cropName || "");
133 formData.append("payload", JSON.stringify(payload || {}));
134
135 const res = await fetch(`${API_URL}/query-similar`, {
136 method: "POST",
137 body: formData,
138 });
139 if (!res.ok) throw new Error(res.statusText);
140 return res.json();
141 },
142
143 /**
144 * Processes voice input
145 */
146 async queryAudio(audioBlob) {
147 if (USE_MOCK_DATA) {
148 await new Promise((r) => setTimeout(r, 1500));
149 return {
150 status: "success",
151 transcription: "Find me healthy tomato crops",
152 results: MOCK_DASHBOARD.slice(0, 2).map((d, i) => ({
153 id: d.id,
154 score: 0.98 - i * 0.05,
155 payload: d.payload,
156 })),
157 };
158 }
159
160 const formData = new FormData();
161 formData.append("file", audioBlob, "recording.webm");
162
163 const res = await fetch(`${API_URL}/query-audio`, {
164 method: "POST",
165 body: formData,
166 });
167 if (!res.ok) throw new Error(res.statusText);
168 return res.json();
169 },
170
171 /**
172 * Ask Demeter a natural language question
173 */
174 async askDemeter(query, context, lang) {
175 const formData = new FormData();
176 formData.append("query", query);
177 formData.append("context", context);
178 formData.append("language", lang);
179
180 const res = await fetch(`${API_URL}/ask-demeter`, {
181 method: "POST",
182 body: formData,
183 });
184 return res.json();
185 },
186};