ranggafermata commited on
Commit
3b4fa03
Β·
verified Β·
1 Parent(s): 061eb7b

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -0
app.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.responses import JSONResponse
3
+ import gradio as gr
4
+ from huggingface_hub import login
5
+ import os
6
+
7
+ login(token=os.getenv("HF_TOKEN"))
8
+
9
+ from transformers import AutoTokenizer, AutoModelForCausalLM
10
+ import torch
11
+
12
+ model_id = "ranggafermata/Fermata"
13
+ tokenizer = AutoTokenizer.from_pretrained(model_id)
14
+ model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32)
15
+
16
+ app = FastAPI()
17
+
18
+ def chat_function(message):
19
+ inputs = tokenizer(message, return_tensors="pt").to(model.device)
20
+ outputs = model.generate(**inputs, max_new_tokens=128)
21
+ return tokenizer.decode(outputs[0], skip_special_tokens=True)
22
+
23
+ @app.post("/chat")
24
+ async def chat_api(request: Request):
25
+ try:
26
+ body = await request.json()
27
+ prompt = body.get("input", "")
28
+ if not prompt:
29
+ return JSONResponse(content={"error": "Missing input"}, status_code=400)
30
+ output = chat_function(prompt)
31
+ return JSONResponse(content={"output": output})
32
+ except Exception as e:
33
+ return JSONResponse(content={"error": str(e)}, status_code=500)
34
+
35
+ # Weather API
36
+ def get_weather(location):
37
+ key = os.getenv("OPENWEATHER_API_KEY")
38
+ if not key:
39
+ return "Missing API key for weather."
40
+ try:
41
+ url = f"http://api.openweathermap.org/data/2.5/weather?q={location}&appid={key}&units=metric"
42
+ r = requests.get(url).json()
43
+ return f"{r['name']}: {r['main']['temp']}Β°C, {r['weather'][0]['description']}"
44
+ except:
45
+ return "Failed to fetch weather."
46
+
47
+ # NASA API
48
+ def get_apod():
49
+ key = os.getenv("NASA_API_KEY")
50
+ if not key:
51
+ return "Missing API key for NASA."
52
+ try:
53
+ r = requests.get(f"https://api.nasa.gov/planetary/apod?api_key={key}").json()
54
+ return f"πŸ“· {r['title']}\n\n{r['explanation']}\n\nMedia: {r['url']}"
55
+ except:
56
+ return "Failed to fetch NASA APOD."
57
+
58
+ # Parse tool call JSON inside [TOOL_CALL] {...}
59
+ def parse_tool_call(output):
60
+ if not output or "[TOOL_CALL]" not in output:
61
+ return None
62
+
63
+ match = re.search(r"\[TOOL_CALL\]\s*(\{.*?\})", output, re.DOTALL)
64
+ if not match:
65
+ return None
66
+
67
+ json_str = match.group(1).strip()
68
+ if not json_str or json_str in ["null", "None", ""]:
69
+ return None
70
+
71
+ try:
72
+ return json.loads(json_str)
73
+ except json.JSONDecodeError as e:
74
+ print(f"❌ JSON parsing failed: {e}")
75
+ print(f"⚠️ Bad JSON string: {json_str}")
76
+ return None
77
+
78
+
79
+ # Chat logic
80
+ def respond(message):
81
+ prompt = f"### Human:\n{message}\n\n### Assistant:"
82
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
83
+ outputs = model.generate(**inputs, max_new_tokens=256, temperature=0.7, do_sample=True)
84
+ result = tokenizer.decode(outputs[0], skip_special_tokens=True)
85
+ reply = result.split("### Assistant:")[-1].strip()
86
+
87
+
88
+ tool = parse_tool_call(reply)
89
+ if tool:
90
+ if tool["tool"] == "get_weather":
91
+ return get_weather(tool.get("location", "Unknown"))
92
+ elif tool["tool"] == "get_apod":
93
+ return get_apod()
94
+ else:
95
+ return f"Tool not recognized: {tool['tool']}"
96
+ return reply
97
+
98
+ # UI
99
+ gr.Interface(
100
+ fn=respond,
101
+ inputs=gr.Textbox(lines=2, placeholder="Ask me something..."),
102
+ outputs="text",
103
+ title="Fermata AI 1.0",
104
+ description="Now powered by the official Gemma 3 model. Ask about the weather or NASA's daily space image!",
105
+ ).launch()