jeeltcraft commited on
Commit
8e2d3ce
·
verified ·
1 Parent(s): 8dabf79

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +260 -102
main.py CHANGED
@@ -1,7 +1,6 @@
1
- from fastapi import FastAPI, HTTPException, Depends
2
  from pydantic import BaseModel
3
- from typing import List, Optional, Dict, Any
4
- from web3 import Web3
5
  from ctransformers import AutoModelForCausalLM
6
  import os
7
  import uuid
@@ -10,13 +9,13 @@ import re
10
 
11
  # ============== Pydantic Models ==============
12
 
13
- class TransferRequest(BaseModel):
14
- recipient_address: str
15
- execute_transfer: bool = False
16
-
17
  class Validation(BaseModel):
18
  prompt: str
19
 
 
 
 
 
20
  # OpenAI-compatible models
21
  class Message(BaseModel):
22
  role: str
@@ -54,16 +53,21 @@ app = FastAPI(
54
  description="""
55
  ## Luminous Coding Assistant API
56
 
57
- OpenAI-compatible API powered by Qwen2.5-Coder-7B for code generation and assistance, you can run this in the cursor agent view.
58
 
59
  ### Features
60
- * 🤖 AI-powered code generation
61
- * 🔗 Web3 integration for Base network
62
- * 📊 Counter utilities
63
- * 💰 ETH transfer capabilities x402 (todo)
 
 
 
 
 
 
64
  """,
65
- version="1.1.0",
66
- terms_of_service="https://huggingface.co/spaces/jeeltcraft/Luminous",
67
  contact={
68
  "name": "Jeeltcraft",
69
  "url": "https://huggingface.co/jeeltcraft",
@@ -82,26 +86,18 @@ app = FastAPI(
82
  },
83
  {
84
  "name": "Utilities",
85
- "description": "Counter and helper functions",
86
- },
87
- {
88
- "name": "Web3",
89
- "description": "Blockchain x402 operations on Base network",
90
  },
91
  ],
92
  swagger_ui_parameters={
93
  "deepLinking": True,
94
  "displayRequestDuration": True,
95
- "docExpansion": "none", # Collapse all sections by default
96
- "syntaxHighlight.theme": "monokai", # Code highlighting theme
97
  "defaultModelsExpandDepth": 2,
98
  }
99
  )
100
 
101
-
102
- # Global counter
103
- counter = 0
104
-
105
  # Global variable to hold the model
106
  _llm_model = None
107
 
@@ -155,54 +151,89 @@ def call_llm(prompt: str) -> str:
155
 
156
  # ============== Helper Functions ==============
157
 
158
- def increment_and_print(value: int):
159
- global counter
160
- counter += value
161
- print(f"Counter: {counter}")
162
- return counter
163
-
164
- # ============== Web3 Functions ==============
165
-
166
- # Web3 setup
167
- BASE_RPC_URL = "https://mainnet.base.org"
168
- w3 = Web3(Web3.HTTPProvider(BASE_RPC_URL))
169
-
170
- def get_private_key():
171
- private_key = os.environ.get("LumKey")
172
- if not private_key:
173
- raise HTTPException(status_code=500, detail="Private key not found in secrets.")
174
- return private_key
175
-
176
- def transfer_eth(recipient_address: str, private_key: str):
177
- try:
178
- account = w3.eth.account.from_key(private_key)
179
- sender_address = account.address
180
-
181
- nonce = w3.eth.get_transaction_count(sender_address)
182
-
183
- tx = {
184
- 'nonce': nonce,
185
- 'to': w3.to_checksum_address(recipient_address),
186
- 'value': w3.to_wei(0.001, 'ether'),
187
- 'gas': 21000,
188
- 'gasPrice': w3.eth.gas_price,
189
- 'chainId': 8453,
 
 
 
 
 
 
 
190
  }
191
-
192
- signed_tx = account.sign_transaction(tx)
193
- tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
194
-
195
- return w3.to_hex(tx_hash)
196
-
197
- except Exception as e:
198
- raise HTTPException(status_code=500, detail=f"Transaction failed: {str(e)}")
199
 
200
  # ============== OpenAI-Compatible Endpoints ==============
201
 
202
- @app.post("/v1/chat/completions", response_model=ChatCompletionResponse)
 
 
 
 
 
 
203
  async def chat_completions(request: ChatCompletionRequest):
204
  """
205
- OpenAI-compatible chat completions endpoint for Cursor integration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  """
207
  try:
208
  # Extract the last user message from conversation history
@@ -212,7 +243,6 @@ async def chat_completions(request: ChatCompletionRequest):
212
  )
213
 
214
  # Format prompt for Qwen2.5-Coder
215
- # Qwen uses a different format than Zephyr
216
  formatted_prompt = f"<|im_start|>system\nYou are a helpful coding assistant.<|im_end|>\n<|im_start|>user\n{user_message}<|im_end|>\n<|im_start|>assistant\n"
217
 
218
  # Call your LLM
@@ -242,10 +272,18 @@ async def chat_completions(request: ChatCompletionRequest):
242
  except Exception as e:
243
  raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
244
 
245
- @app.get("/v1/models")
 
 
 
 
 
246
  async def list_models():
247
  """
248
- OpenAI-compatible models endpoint
 
 
 
249
  """
250
  return {
251
  "object": "list",
@@ -259,56 +297,176 @@ async def list_models():
259
  ]
260
  }
261
 
262
- # ============== Original Endpoints ==============
263
 
264
- @app.post("/llm_on_cpu")
 
 
 
 
 
265
  async def stream(item: Validation):
 
 
 
 
 
 
 
 
266
  system_prompt = 'Below is an instruction that describes a task. Write a response that appropriately completes the request.'
267
  E_INST = "</s>"
268
  user, assistant = "<|user|>", "<|assistant|>"
269
  prompt = f"{system_prompt}{E_INST}\n{user}\n{item.prompt.strip()}{E_INST}\n{assistant}\n"
270
  return {"response": call_llm(prompt)}
271
 
272
- @app.post("/increment_from_prompt")
273
- async def increment_from_prompt(item: Validation):
274
- match = re.search(r'\d+', item.prompt)
275
- if match:
276
- increment_value = int(match.group())
277
- result = increment_and_print(increment_value)
278
- else:
279
- result = increment_and_print(0)
280
- return {"counter": result}
281
 
282
- @app.post("/increment_counter")
283
- async def increment():
284
- result = increment_and_print(1)
285
- return {"counter": result}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
- @app.post("/reward")
288
- async def reward_endpoint(request: TransferRequest, private_key: str = Depends(get_private_key)):
289
- if request.execute_transfer:
290
- try:
291
- tx_hash = transfer_eth(request.recipient_address, private_key)
292
- return {"transaction_hash": tx_hash}
293
- except HTTPException as e:
294
- raise e
295
- except Exception as e:
296
- raise HTTPException(status_code=500, detail=str(e))
297
- else:
298
- return {"message": "Transaction blocked by execute_transfer flag."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
- @app.get("/")
 
 
 
 
 
 
 
301
  async def root():
 
 
 
 
 
302
  return {
303
  "message": "Luminous API - OpenAI Compatible Coding Assistant",
304
  "status": "active",
305
- "model": "Qwen2.5-Coder-7B-Instruct"
 
 
306
  }
307
 
308
- @app.get("/health")
 
 
 
 
 
309
  async def health_check():
 
 
 
 
 
310
  return {
311
  "status": "healthy",
312
  "model_loaded": _llm_model is not None,
313
- "counter": counter
314
- }
 
1
+ from fastapi import FastAPI, HTTPException
2
  from pydantic import BaseModel
3
+ from typing import List, Optional
 
4
  from ctransformers import AutoModelForCausalLM
5
  import os
6
  import uuid
 
9
 
10
  # ============== Pydantic Models ==============
11
 
 
 
 
 
12
  class Validation(BaseModel):
13
  prompt: str
14
 
15
+ class EthConversionRequest(BaseModel):
16
+ value: float
17
+ from_unit: str = "eth" # eth, gwei, or wei
18
+
19
  # OpenAI-compatible models
20
  class Message(BaseModel):
21
  role: str
 
53
  description="""
54
  ## Luminous Coding Assistant API
55
 
56
+ OpenAI-compatible API powered by Qwen2.5-Coder-7B for code generation and assistance.
57
 
58
  ### Features
59
+ * 🤖 AI-powered code generation with Qwen2.5-Coder
60
+ * 🔌 OpenAI-compatible endpoints for Cursor IDE integration
61
+ * 💰 ETH unit conversion utilities (Wei ↔ Gwei ↔ ETH)
62
+ * 💻 Optimized for coding tasks and assistance
63
+
64
+ ### Integration with Cursor IDE
65
+ 1. Go to Cursor Settings → Models → Override OpenAI Base URL
66
+ 2. Set Base URL: `https://jeeltcraft-luminous.hf.space/v1`
67
+ 3. Model name: `qwen2.5-coder-7b`
68
+ 4. Add any dummy API key
69
  """,
70
+ version="1.0.0",
 
71
  contact={
72
  "name": "Jeeltcraft",
73
  "url": "https://huggingface.co/jeeltcraft",
 
86
  },
87
  {
88
  "name": "Utilities",
89
+ "description": "ETH conversion and helper functions",
 
 
 
 
90
  },
91
  ],
92
  swagger_ui_parameters={
93
  "deepLinking": True,
94
  "displayRequestDuration": True,
95
+ "docExpansion": "none",
96
+ "syntaxHighlight.theme": "monokai",
97
  "defaultModelsExpandDepth": 2,
98
  }
99
  )
100
 
 
 
 
 
101
  # Global variable to hold the model
102
  _llm_model = None
103
 
 
151
 
152
  # ============== Helper Functions ==============
153
 
154
+ def convert_eth_units(value: float, from_unit: str = "eth") -> dict:
155
+ """
156
+ Convert ETH value to wei and gwei.
157
+
158
+ Args:
159
+ value: The numeric value to convert
160
+ from_unit: The source unit ('eth', 'gwei', or 'wei')
161
+
162
+ Returns:
163
+ Dictionary with conversions to all units
164
+ """
165
+ # Convert input to wei first
166
+ if from_unit.lower() == "eth":
167
+ wei_value = int(value * 10**18)
168
+ elif from_unit.lower() == "gwei":
169
+ wei_value = int(value * 10**9)
170
+ elif from_unit.lower() == "wei":
171
+ wei_value = int(value)
172
+ else:
173
+ raise ValueError("Invalid unit. Use 'eth', 'gwei', or 'wei'")
174
+
175
+ # Convert wei to all units
176
+ eth_value = wei_value / 10**18
177
+ gwei_value = wei_value / 10**9
178
+
179
+ return {
180
+ "input": {
181
+ "value": value,
182
+ "unit": from_unit
183
+ },
184
+ "conversions": {
185
+ "wei": str(wei_value), # String to avoid JavaScript number overflow
186
+ "gwei": gwei_value,
187
+ "eth": eth_value
188
+ },
189
+ "formatted": {
190
+ "wei": f"{wei_value:,} wei",
191
+ "gwei": f"{gwei_value:,.2f} gwei",
192
+ "eth": f"{eth_value:.18f} ETH"
193
  }
194
+ }
 
 
 
 
 
 
 
195
 
196
  # ============== OpenAI-Compatible Endpoints ==============
197
 
198
+ @app.post(
199
+ "/v1/chat/completions",
200
+ response_model=ChatCompletionResponse,
201
+ tags=["OpenAI Compatible"],
202
+ summary="Create chat completion",
203
+ response_description="Returns the model's response to the conversation"
204
+ )
205
  async def chat_completions(request: ChatCompletionRequest):
206
  """
207
+ Create a chat completion using OpenAI-compatible format.
208
+
209
+ This endpoint is designed for integration with Cursor IDE and other
210
+ OpenAI-compatible clients. It accepts a conversation history and returns
211
+ the model's response.
212
+
213
+ ## Parameters
214
+ - **model**: Model identifier (use `qwen2.5-coder-7b` for this API)
215
+ - **messages**: Array of conversation messages with role and content
216
+ - Role can be: `system`, `user`, or `assistant`
217
+ - **temperature**: Controls randomness (0.0 = deterministic, 2.0 = very random)
218
+ - **max_tokens**: Maximum number of tokens to generate in the response
219
+ - **stream**: Whether to stream the response (not yet implemented)
220
+
221
+ ## Example Request
222
+ ```json
223
+ {
224
+ "model": "qwen2.5-coder-7b",
225
+ "messages": [
226
+ {"role": "system", "content": "You are a helpful coding assistant."},
227
+ {"role": "user", "content": "Write a Python function to reverse a string"}
228
+ ],
229
+ "temperature": 0.7,
230
+ "max_tokens": 512
231
+ }
232
+ ```
233
+
234
+ ## Returns
235
+ A chat completion response with the model's generated text, token usage,
236
+ and other metadata.
237
  """
238
  try:
239
  # Extract the last user message from conversation history
 
243
  )
244
 
245
  # Format prompt for Qwen2.5-Coder
 
246
  formatted_prompt = f"<|im_start|>system\nYou are a helpful coding assistant.<|im_end|>\n<|im_start|>user\n{user_message}<|im_end|>\n<|im_start|>assistant\n"
247
 
248
  # Call your LLM
 
272
  except Exception as e:
273
  raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
274
 
275
+ @app.get(
276
+ "/v1/models",
277
+ tags=["OpenAI Compatible"],
278
+ summary="List available models",
279
+ response_description="Returns a list of available models"
280
+ )
281
  async def list_models():
282
  """
283
+ List all available models in OpenAI-compatible format.
284
+
285
+ This endpoint returns the models available through this API.
286
+ Use the model ID when making requests to `/v1/chat/completions`.
287
  """
288
  return {
289
  "object": "list",
 
297
  ]
298
  }
299
 
300
+ # ============== Direct LLM Endpoints ==============
301
 
302
+ @app.post(
303
+ "/llm_on_cpu",
304
+ tags=["LLM"],
305
+ summary="Direct LLM inference",
306
+ response_description="Returns the model's raw response"
307
+ )
308
  async def stream(item: Validation):
309
+ """
310
+ Direct inference endpoint for simple prompts.
311
+
312
+ This endpoint provides direct access to the LLM without the OpenAI wrapper.
313
+ Useful for custom prompt formatting.
314
+
315
+ - **prompt**: Your input text prompt
316
+ """
317
  system_prompt = 'Below is an instruction that describes a task. Write a response that appropriately completes the request.'
318
  E_INST = "</s>"
319
  user, assistant = "<|user|>", "<|assistant|>"
320
  prompt = f"{system_prompt}{E_INST}\n{user}\n{item.prompt.strip()}{E_INST}\n{assistant}\n"
321
  return {"response": call_llm(prompt)}
322
 
323
+ # ============== Utility Endpoints ==============
 
 
 
 
 
 
 
 
324
 
325
+ @app.post(
326
+ "/convert_eth_units",
327
+ tags=["Utilities"],
328
+ summary="Convert ETH units (ETH ↔ Gwei ↔ Wei)",
329
+ response_description="Returns conversions to all ETH units"
330
+ )
331
+ async def convert_units(request: EthConversionRequest):
332
+ """
333
+ Convert between Ethereum units: ETH, Gwei, and Wei.
334
+
335
+ ## Ethereum Units Explained
336
+ - **ETH**: The base unit (1 ETH = 1,000,000,000,000,000,000 wei)
337
+ - **Gwei**: Gigawei, commonly used for gas prices (1 Gwei = 1,000,000,000 wei)
338
+ - **Wei**: The smallest unit of Ether (1 wei = 0.000000000000000001 ETH)
339
+
340
+ ## Parameters
341
+ - **value**: The numeric value to convert
342
+ - **from_unit**: Source unit - `eth`, `gwei`, or `wei` (default: `eth`)
343
+
344
+ ## Example Requests
345
+
346
+ Convert 1 ETH to all units:
347
+ ```json
348
+ {
349
+ "value": 1,
350
+ "from_unit": "eth"
351
+ }
352
+ ```
353
+
354
+ Convert 50 Gwei to all units:
355
+ ```json
356
+ {
357
+ "value": 50,
358
+ "from_unit": "gwei"
359
+ }
360
+ ```
361
+
362
+ ## Returns
363
+ Conversions to Wei, Gwei, and ETH with both numeric and formatted values.
364
+ """
365
+ try:
366
+ result = convert_eth_units(request.value, request.from_unit)
367
+ return result
368
+ except ValueError as e:
369
+ raise HTTPException(status_code=400, detail=str(e))
370
+ except Exception as e:
371
+ raise HTTPException(status_code=500, detail=f"Conversion error: {str(e)}")
372
 
373
+ @app.post(
374
+ "/eth_to_units",
375
+ tags=["Utilities"],
376
+ summary="Quick convert: ETH to Wei/Gwei",
377
+ response_description="Returns Wei and Gwei values"
378
+ )
379
+ async def eth_to_units(item: Validation):
380
+ """
381
+ Quick converter: Extract a number from text and convert from ETH to Wei and Gwei.
382
+
383
+ This endpoint extracts the first number found in the prompt and treats it as ETH,
384
+ then converts it to Wei and Gwei. Useful for quick conversions in chat interfaces.
385
+
386
+ ## Example
387
+ Send prompt: `"Convert 0.5 ETH"` or just `"0.5"`
388
+
389
+ Returns the value in Wei and Gwei.
390
+
391
+ - **prompt**: Text containing an ETH amount (number will be extracted)
392
+ """
393
+ try:
394
+ # Extract number from prompt
395
+ match = re.search(r'\d+\.?\d*', item.prompt)
396
+ if match:
397
+ eth_value = float(match.group())
398
+ result = convert_eth_units(eth_value, "eth")
399
+ return result
400
+ else:
401
+ raise HTTPException(status_code=400, detail="No numeric value found in prompt")
402
+ except ValueError as e:
403
+ raise HTTPException(status_code=400, detail=str(e))
404
+ except Exception as e:
405
+ raise HTTPException(status_code=500, detail=f"Conversion error: {str(e)}")
406
+
407
+ @app.get(
408
+ "/quick_convert/{value}/{unit}",
409
+ tags=["Utilities"],
410
+ summary="Quick URL-based ETH conversion",
411
+ response_description="Returns conversions to all units"
412
+ )
413
+ async def quick_convert(value: float, unit: str = "eth"):
414
+ """
415
+ Quick conversion via URL path parameters.
416
+
417
+ ## Usage Examples
418
+ - `/quick_convert/1/eth` - Convert 1 ETH to Wei and Gwei
419
+ - `/quick_convert/50/gwei` - Convert 50 Gwei to ETH and Wei
420
+ - `/quick_convert/1000000000/wei` - Convert 1,000,000,000 Wei to ETH and Gwei
421
+
422
+ ## Parameters
423
+ - **value**: Numeric amount to convert
424
+ - **unit**: Source unit (`eth`, `gwei`, or `wei`)
425
+ """
426
+ try:
427
+ result = convert_eth_units(value, unit)
428
+ return result
429
+ except ValueError as e:
430
+ raise HTTPException(status_code=400, detail=str(e))
431
+ except Exception as e:
432
+ raise HTTPException(status_code=500, detail=f"Conversion error: {str(e)}")
433
 
434
+ # ============== Health & Info Endpoints ==============
435
+
436
+ @app.get(
437
+ "/",
438
+ tags=["Utilities"],
439
+ summary="API root information",
440
+ response_description="Returns API status and information"
441
+ )
442
  async def root():
443
+ """
444
+ Get basic information about the API.
445
+
446
+ Returns the API name, status, and current model being used.
447
+ """
448
  return {
449
  "message": "Luminous API - OpenAI Compatible Coding Assistant",
450
  "status": "active",
451
+ "model": "Qwen2.5-Coder-7B-Instruct",
452
+ "docs": "/docs",
453
+ "openapi": "/openapi.json"
454
  }
455
 
456
+ @app.get(
457
+ "/health",
458
+ tags=["Utilities"],
459
+ summary="Health check",
460
+ response_description="Returns health status and diagnostics"
461
+ )
462
  async def health_check():
463
+ """
464
+ Check the health status of the API.
465
+
466
+ Returns information about model loading status.
467
+ """
468
  return {
469
  "status": "healthy",
470
  "model_loaded": _llm_model is not None,
471
+ "api_version": "1.0.0"
472
+ }