smirkd commited on
Commit
ddff1e8
·
1 Parent(s): 455f38d

Configure custom UIGENT-30B model endpoint

Browse files

- Replace all models with Tesslate/UIGENT-30B-3A-Preview as the only model
- Add tesslate provider configuration
- Implement custom OpenAI-compatible endpoint support in ask-ai route
- Update README with Hugging Face Spaces setup instructions
- All endpoint URLs and API keys are now environment variables only

Files changed (3) hide show
  1. README.md +53 -4
  2. app/api/ask-ai/route.ts +214 -54
  3. lib/providers.ts +7 -47
README.md CHANGED
@@ -7,16 +7,65 @@ sdk: docker
7
  pinned: true
8
  app_port: 3000
9
  license: mit
10
- short_description: Generate any application with DeepSeek
11
  models:
12
- - deepseek-ai/DeepSeek-V3-0324
13
- - deepseek-ai/DeepSeek-R1-0528
14
  ---
15
 
16
  # DeepSite 🐳
17
 
18
- DeepSite is a coding platform powered by DeepSeek AI, designed to make coding smarter and more efficient. Tailored for developers, data scientists, and AI engineers, it integrates generative AI into your coding projects to enhance creativity and productivity.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  ## How to use it locally
21
 
22
  Follow [this discussion](https://huggingface.co/spaces/enzostvs/deepsite/discussions/74)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  pinned: true
8
  app_port: 3000
9
  license: mit
10
+ short_description: Generate any application with UIGENT-30B
11
  models:
12
+ - Tesslate/UIGENT-30B-3A-Preview
 
13
  ---
14
 
15
  # DeepSite 🐳
16
 
17
+ DeepSite is a coding platform powered by UIGENT-30B AI, designed to make coding smarter and more efficient. Tailored for developers, data scientists, and AI engineers, it integrates generative AI into your coding projects to enhance creativity and productivity.
18
+
19
+ ## Hugging Face Spaces Setup
20
+
21
+ This Space is configured to use a custom model endpoint. To set up your own instance:
22
+
23
+ ### Required Environment Variables (Secrets)
24
+
25
+ In your Hugging Face Space settings, add the following secrets:
26
+
27
+ 1. **CUSTOM_MODEL_ENDPOINT** (required - set as secret)
28
+ - The OpenAI-compatible endpoint for your custom model
29
+ - Example: `https://your-endpoint.com/v1`
30
+ - **Important**: This should be set as a Hugging Face Space secret, not in the code
31
+
32
+ 2. **CUSTOM_MODEL_API_KEY** (set as secret)
33
+ - Your API key for authenticating with the custom model endpoint
34
+ - Leave empty if no authentication is required
35
+ - **Important**: This should be set as a Hugging Face Space secret, not in the code
36
+
37
+ 3. **MONGODB_URI** (required)
38
+ - Your MongoDB connection string for storing projects and user data
39
+ - Example: `mongodb+srv://user:[email protected]/deepsite`
40
+
41
+ ### How to Add Secrets
42
+
43
+ 1. Go to your Space settings
44
+ 2. Navigate to "Repository secrets"
45
+ 3. Add each secret with its corresponding value
46
+ 4. The Space will automatically restart with the new configuration
47
 
48
  ## How to use it locally
49
 
50
  Follow [this discussion](https://huggingface.co/spaces/enzostvs/deepsite/discussions/74)
51
+
52
+ ### Local Setup
53
+
54
+ 1. Clone the repository
55
+ 2. Copy `.env.example` to `.env` and fill in your values:
56
+ ```bash
57
+ cp .env.example .env
58
+ ```
59
+ 3. Install dependencies:
60
+ ```bash
61
+ npm install
62
+ ```
63
+ 4. Run the development server:
64
+ ```bash
65
+ npm run dev
66
+ ```
67
+ 5. Open [http://localhost:3000](http://localhost:3000) in your browser
68
+
69
+ ### Environment Variables for Local Development
70
+
71
+ See `.env.example` for all required environment variables.
app/api/ask-ai/route.ts CHANGED
@@ -85,7 +85,7 @@ export async function POST(request: NextRequest) {
85
  billTo = "huggingface";
86
  }
87
 
88
- const DEFAULT_PROVIDER = PROVIDERS.novita;
89
  const selectedProvider =
90
  provider === "auto"
91
  ? PROVIDERS[selectedModel.autoProvider as keyof typeof PROVIDERS]
@@ -109,11 +109,17 @@ export async function POST(request: NextRequest) {
109
  (async () => {
110
  let completeResponse = "";
111
  try {
112
- const client = new InferenceClient(token);
113
- const chatCompletion = client.chatCompletionStream(
114
- {
 
 
 
 
 
 
 
115
  model: selectedModel.value,
116
- provider: selectedProvider.id as any,
117
  messages: [
118
  {
119
  role: "system",
@@ -129,48 +135,131 @@ export async function POST(request: NextRequest) {
129
  },
130
  ],
131
  max_tokens: selectedProvider.max_tokens,
132
- },
133
- billTo ? { billTo } : {}
134
- );
 
 
 
135
 
136
- while (true) {
137
- const { done, value } = await chatCompletion.next();
138
- if (done) {
139
- break;
140
  }
141
 
142
- const chunk = value.choices[0]?.delta?.content;
143
- if (chunk) {
144
- let newChunk = chunk;
145
- if (!selectedModel?.isThinker) {
146
- if (provider !== "sambanova") {
147
- await writer.write(encoder.encode(chunk));
148
- completeResponse += chunk;
149
 
150
- if (completeResponse.includes("</html>")) {
151
- break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  }
153
- } else {
154
- if (chunk.includes("</html>")) {
155
- newChunk = newChunk.replace(/<\/html>[\s\S]*/, "</html>");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  }
 
 
 
157
  completeResponse += newChunk;
158
  await writer.write(encoder.encode(newChunk));
159
- if (newChunk.includes("</html>")) {
160
- break;
161
- }
162
- }
163
- } else {
164
- const lastThinkTagIndex =
165
- completeResponse.lastIndexOf("</think>");
166
- completeResponse += newChunk;
167
- await writer.write(encoder.encode(newChunk));
168
- if (lastThinkTagIndex !== -1) {
169
- const afterLastThinkTag = completeResponse.slice(
170
- lastThinkTagIndex + "</think>".length
171
- );
172
- if (afterLastThinkTag.includes("</html>")) {
173
- break;
174
  }
175
  }
176
  }
@@ -267,19 +356,29 @@ export async function PUT(request: NextRequest) {
267
  billTo = "huggingface";
268
  }
269
 
270
- const client = new InferenceClient(token);
271
-
272
- const DEFAULT_PROVIDER = PROVIDERS.novita;
273
  const selectedProvider =
274
  provider === "auto"
275
  ? PROVIDERS[selectedModel.autoProvider as keyof typeof PROVIDERS]
276
  : PROVIDERS[provider as keyof typeof PROVIDERS] ?? DEFAULT_PROVIDER;
277
 
278
  try {
279
- const response = await client.chatCompletion(
280
- {
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  model: selectedModel.value,
282
- provider: selectedProvider.id as any,
283
  messages: [
284
  {
285
  role: "system",
@@ -293,7 +392,6 @@ export async function PUT(request: NextRequest) {
293
  },
294
  {
295
  role: "assistant",
296
-
297
  content: `The current code is: \n\`\`\`html\n${html}\n\`\`\` ${
298
  selectedElementHtml
299
  ? `\n\nYou have to update ONLY the following element, NOTHING ELSE: \n\n\`\`\`html\n${selectedElementHtml}\n\`\`\``
@@ -305,14 +403,76 @@ export async function PUT(request: NextRequest) {
305
  content: prompt,
306
  },
307
  ],
308
- ...(selectedProvider.id !== "sambanova"
309
- ? {
310
- max_tokens: selectedProvider.max_tokens,
311
- }
312
- : {}),
313
- },
314
- billTo ? { billTo } : {}
315
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
  const chunk = response.choices[0]?.message?.content;
318
  if (!chunk) {
 
85
  billTo = "huggingface";
86
  }
87
 
88
+ const DEFAULT_PROVIDER = PROVIDERS.tesslate;
89
  const selectedProvider =
90
  provider === "auto"
91
  ? PROVIDERS[selectedModel.autoProvider as keyof typeof PROVIDERS]
 
109
  (async () => {
110
  let completeResponse = "";
111
  try {
112
+ // Handle custom Tesslate endpoint
113
+ if (selectedProvider.id === "tesslate") {
114
+ const customEndpoint = process.env.CUSTOM_MODEL_ENDPOINT;
115
+ const customApiKey = process.env.CUSTOM_MODEL_API_KEY;
116
+
117
+ if (!customEndpoint) {
118
+ throw new Error("CUSTOM_MODEL_ENDPOINT environment variable is not set");
119
+ }
120
+
121
+ const requestBody = {
122
  model: selectedModel.value,
 
123
  messages: [
124
  {
125
  role: "system",
 
135
  },
136
  ],
137
  max_tokens: selectedProvider.max_tokens,
138
+ stream: true,
139
+ };
140
+
141
+ const headers: Record<string, string> = {
142
+ "Content-Type": "application/json",
143
+ };
144
 
145
+ if (customApiKey) {
146
+ headers["Authorization"] = `Bearer ${customApiKey}`;
 
 
147
  }
148
 
149
+ const apiResponse = await fetch(`${customEndpoint}/chat/completions`, {
150
+ method: "POST",
151
+ headers,
152
+ body: JSON.stringify(requestBody),
153
+ });
 
 
154
 
155
+ if (!apiResponse.ok) {
156
+ throw new Error(`Custom endpoint error: ${apiResponse.statusText}`);
157
+ }
158
+
159
+ const reader = apiResponse.body?.getReader();
160
+ if (!reader) {
161
+ throw new Error("No response body");
162
+ }
163
+
164
+ const decoder = new TextDecoder();
165
+ while (true) {
166
+ const { done, value } = await reader.read();
167
+ if (done) break;
168
+
169
+ const chunk = decoder.decode(value);
170
+ const lines = chunk.split("\n").filter(line => line.trim() !== "");
171
+
172
+ for (const line of lines) {
173
+ if (line.startsWith("data: ")) {
174
+ const data = line.slice(6);
175
+ if (data === "[DONE]") continue;
176
+
177
+ try {
178
+ const parsed = JSON.parse(data);
179
+ const content = parsed.choices?.[0]?.delta?.content;
180
+ if (content) {
181
+ await writer.write(encoder.encode(content));
182
+ completeResponse += content;
183
+
184
+ if (completeResponse.includes("</html>")) {
185
+ break;
186
+ }
187
+ }
188
+ } catch (e) {
189
+ // Skip invalid JSON
190
  }
191
+ }
192
+ }
193
+
194
+ if (completeResponse.includes("</html>")) {
195
+ break;
196
+ }
197
+ }
198
+ } else {
199
+ // Original HuggingFace logic
200
+ const client = new InferenceClient(token);
201
+ const chatCompletion = client.chatCompletionStream(
202
+ {
203
+ model: selectedModel.value,
204
+ provider: selectedProvider.id as any,
205
+ messages: [
206
+ {
207
+ role: "system",
208
+ content: INITIAL_SYSTEM_PROMPT,
209
+ },
210
+ {
211
+ role: "user",
212
+ content: redesignMarkdown
213
+ ? `Here is my current design as a markdown:\n\n${redesignMarkdown}\n\nNow, please create a new design based on this markdown.`
214
+ : html
215
+ ? `Here is my current HTML code:\n\n\`\`\`html\n${html}\n\`\`\`\n\nNow, please create a new design based on this HTML.`
216
+ : prompt,
217
+ },
218
+ ],
219
+ max_tokens: selectedProvider.max_tokens,
220
+ },
221
+ billTo ? { billTo } : {}
222
+ );
223
+
224
+ while (true) {
225
+ const { done, value } = await chatCompletion.next();
226
+ if (done) {
227
+ break;
228
+ }
229
+
230
+ const chunk = value.choices[0]?.delta?.content;
231
+ if (chunk) {
232
+ let newChunk = chunk;
233
+ if (!selectedModel?.isThinker) {
234
+ if (provider !== "sambanova") {
235
+ await writer.write(encoder.encode(chunk));
236
+ completeResponse += chunk;
237
+
238
+ if (completeResponse.includes("</html>")) {
239
+ break;
240
+ }
241
+ } else {
242
+ if (chunk.includes("</html>")) {
243
+ newChunk = newChunk.replace(/<\/html>[\s\S]*/, "</html>");
244
+ }
245
+ completeResponse += newChunk;
246
+ await writer.write(encoder.encode(newChunk));
247
+ if (newChunk.includes("</html>")) {
248
+ break;
249
+ }
250
  }
251
+ } else {
252
+ const lastThinkTagIndex =
253
+ completeResponse.lastIndexOf("</think>");
254
  completeResponse += newChunk;
255
  await writer.write(encoder.encode(newChunk));
256
+ if (lastThinkTagIndex !== -1) {
257
+ const afterLastThinkTag = completeResponse.slice(
258
+ lastThinkTagIndex + "</think>".length
259
+ );
260
+ if (afterLastThinkTag.includes("</html>")) {
261
+ break;
262
+ }
 
 
 
 
 
 
 
 
263
  }
264
  }
265
  }
 
356
  billTo = "huggingface";
357
  }
358
 
359
+ const DEFAULT_PROVIDER = PROVIDERS.tesslate;
 
 
360
  const selectedProvider =
361
  provider === "auto"
362
  ? PROVIDERS[selectedModel.autoProvider as keyof typeof PROVIDERS]
363
  : PROVIDERS[provider as keyof typeof PROVIDERS] ?? DEFAULT_PROVIDER;
364
 
365
  try {
366
+ let response;
367
+
368
+ // Handle custom Tesslate endpoint
369
+ if (selectedProvider.id === "tesslate") {
370
+ const customEndpoint = process.env.CUSTOM_MODEL_ENDPOINT;
371
+ const customApiKey = process.env.CUSTOM_MODEL_API_KEY;
372
+
373
+ if (!customEndpoint) {
374
+ return NextResponse.json(
375
+ { ok: false, error: "CUSTOM_MODEL_ENDPOINT environment variable is not set" },
376
+ { status: 500 }
377
+ );
378
+ }
379
+
380
+ const requestBody = {
381
  model: selectedModel.value,
 
382
  messages: [
383
  {
384
  role: "system",
 
392
  },
393
  {
394
  role: "assistant",
 
395
  content: `The current code is: \n\`\`\`html\n${html}\n\`\`\` ${
396
  selectedElementHtml
397
  ? `\n\nYou have to update ONLY the following element, NOTHING ELSE: \n\n\`\`\`html\n${selectedElementHtml}\n\`\`\``
 
403
  content: prompt,
404
  },
405
  ],
406
+ max_tokens: selectedProvider.max_tokens,
407
+ };
408
+
409
+ const headers: Record<string, string> = {
410
+ "Content-Type": "application/json",
411
+ };
412
+
413
+ if (customApiKey) {
414
+ headers["Authorization"] = `Bearer ${customApiKey}`;
415
+ }
416
+
417
+ const apiResponse = await fetch(`${customEndpoint}/chat/completions`, {
418
+ method: "POST",
419
+ headers,
420
+ body: JSON.stringify(requestBody),
421
+ });
422
+
423
+ if (!apiResponse.ok) {
424
+ throw new Error(`Custom endpoint error: ${apiResponse.statusText}`);
425
+ }
426
+
427
+ const data = await apiResponse.json();
428
+ response = {
429
+ choices: [{
430
+ message: {
431
+ content: data.choices?.[0]?.message?.content || ""
432
+ }
433
+ }]
434
+ };
435
+ } else {
436
+ // Original HuggingFace logic
437
+ const client = new InferenceClient(token);
438
+ response = await client.chatCompletion(
439
+ {
440
+ model: selectedModel.value,
441
+ provider: selectedProvider.id as any,
442
+ messages: [
443
+ {
444
+ role: "system",
445
+ content: FOLLOW_UP_SYSTEM_PROMPT,
446
+ },
447
+ {
448
+ role: "user",
449
+ content: previousPrompt
450
+ ? previousPrompt
451
+ : "You are modifying the HTML file based on the user's request.",
452
+ },
453
+ {
454
+ role: "assistant",
455
+
456
+ content: `The current code is: \n\`\`\`html\n${html}\n\`\`\` ${
457
+ selectedElementHtml
458
+ ? `\n\nYou have to update ONLY the following element, NOTHING ELSE: \n\n\`\`\`html\n${selectedElementHtml}\n\`\`\``
459
+ : ""
460
+ }`,
461
+ },
462
+ {
463
+ role: "user",
464
+ content: prompt,
465
+ },
466
+ ],
467
+ ...(selectedProvider.id !== "sambanova"
468
+ ? {
469
+ max_tokens: selectedProvider.max_tokens,
470
+ }
471
+ : {}),
472
+ },
473
+ billTo ? { billTo } : {}
474
+ );
475
+ }
476
 
477
  const chunk = response.choices[0]?.message?.content;
478
  if (!chunk) {
lib/providers.ts CHANGED
@@ -1,56 +1,16 @@
1
  export const PROVIDERS = {
2
- "fireworks-ai": {
3
- name: "Fireworks AI",
4
- max_tokens: 131_000,
5
- id: "fireworks-ai",
6
- },
7
- nebius: {
8
- name: "Nebius AI Studio",
9
- max_tokens: 131_000,
10
- id: "nebius",
11
- },
12
- sambanova: {
13
- name: "SambaNova",
14
  max_tokens: 32_000,
15
- id: "sambanova",
16
- },
17
- novita: {
18
- name: "NovitaAI",
19
- max_tokens: 16_000,
20
- id: "novita",
21
- },
22
- hyperbolic: {
23
- name: "Hyperbolic",
24
- max_tokens: 131_000,
25
- id: "hyperbolic",
26
- },
27
- together: {
28
- name: "Together AI",
29
- max_tokens: 128_000,
30
- id: "together",
31
  },
32
  };
33
 
34
  export const MODELS = [
35
  {
36
- value: "deepseek-ai/DeepSeek-V3-0324",
37
- label: "DeepSeek V3 O324",
38
- providers: ["fireworks-ai", "nebius", "sambanova", "novita", "hyperbolic"],
39
- autoProvider: "novita",
40
- },
41
- {
42
- value: "deepseek-ai/DeepSeek-R1-0528",
43
- label: "DeepSeek R1 0528",
44
- providers: [
45
- "fireworks-ai",
46
- "novita",
47
- "hyperbolic",
48
- "nebius",
49
- "together",
50
- "sambanova",
51
- ],
52
- autoProvider: "novita",
53
- isNew: true,
54
- isThinker: true,
55
  },
56
  ];
 
1
  export const PROVIDERS = {
2
+ tesslate: {
3
+ name: "Tesslate",
 
 
 
 
 
 
 
 
 
 
4
  max_tokens: 32_000,
5
+ id: "tesslate",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  },
7
  };
8
 
9
  export const MODELS = [
10
  {
11
+ value: "Tesslate/UIGENT-30B-3A-Preview",
12
+ label: "UIGENT-30B-3A Preview",
13
+ providers: ["tesslate"],
14
+ autoProvider: "tesslate",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  },
16
  ];