CORVO-AI commited on
Commit
4137d75
·
verified ·
1 Parent(s): 7696d99

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -36
app.py CHANGED
@@ -3,7 +3,7 @@ import base64
3
  import requests
4
  from pathlib import Path
5
  from typing import Optional, List, Dict, Any
6
- from flask import Flask, request, Response
7
 
8
  app = Flask(__name__)
9
 
@@ -24,6 +24,12 @@ def encode_image_to_base64_from_url(image_url: str, timeout: int = 30) -> str:
24
  return base64.b64encode(resp.content).decode("utf-8")
25
 
26
  def normalize_messages_to_prompt(messages: List[Dict[str, str]]) -> str:
 
 
 
 
 
 
27
  lines = []
28
  for msg in messages:
29
  role = str(msg.get("role", "")).strip().lower()
@@ -37,20 +43,25 @@ def normalize_messages_to_prompt(messages: List[Dict[str, str]]) -> str:
37
  else:
38
  prefix = "User"
39
  lines.append(f"{prefix}: {content}")
 
40
  return "\n".join(lines).strip()
41
 
42
  def extract_output(obj: dict) -> str:
 
 
43
  try:
44
  out = obj["data"]["outputs"][0]["output"]
45
  if isinstance(out, str):
46
  return out
47
  except Exception:
48
  pass
 
49
  if isinstance(obj, dict) and isinstance(obj.get("output"), str):
50
  return obj["output"]
 
51
  return ""
52
 
53
- def call_roboflow_playground(image_b64: str, prompt: str, model: str = DEFAULT_MODEL, timeout: int = 60) -> dict:
54
  payload = {
55
  "inputs": {
56
  "image": {"type": "base64", "value": image_b64},
@@ -73,66 +84,60 @@ def call_roboflow_playground(image_b64: str, prompt: str, model: str = DEFAULT_M
73
  @app.route("/infer", methods=["POST"])
74
  def infer():
75
  try:
76
- data = request.get_json(force=True) or {}
77
 
78
- # Image input
79
  image_b64 = (data.get("image_base64") or "").strip()
80
  image_url = (data.get("image_url") or "").strip()
81
  image_path = (data.get("image_path") or "").strip()
82
 
83
  if not image_b64:
84
  if image_url:
85
- image_b64 = encode_image_to_base64_from_url(image_url)
 
 
 
86
  elif image_path:
87
- image_b64 = encode_image_to_base64_from_path(image_path)
 
 
 
88
  else:
89
- return Response("Missing image. Provide image_base64, image_url, or image_path.", status=400)
90
 
91
- # Messages -> prompt
92
  messages = data.get("messages", [])
93
- if not isinstance(messages, list):
94
- return Response("messages must be a list of {role, content}.", status=400)
95
 
96
  prompt = normalize_messages_to_prompt(messages)
97
  if not prompt:
98
- return Response("messages is empty or has no content.", status=400)
99
 
 
100
  model = (data.get("model") or DEFAULT_MODEL).strip() or DEFAULT_MODEL
101
 
102
- upstream = call_roboflow_playground(image_b64=image_b64, prompt=prompt, model=model)
103
- output_text = extract_output(upstream).strip()
104
 
105
- if not output_text:
106
- return Response("No output from model.", status=502)
107
 
108
- # Return only the AI response as plain text
109
- return Response(output_text, mimetype="text/plain", status=200)
 
 
110
 
111
  except requests.HTTPError as e:
112
  status = getattr(e.response, "status_code", 502)
113
  text = getattr(e.response, "text", "")
114
- return Response(f"Upstream HTTP error ({status}): {text}", status=502)
115
  except Exception as e:
116
- return Response(str(e), status=500)
117
 
118
  def main():
 
119
  app.run(host="0.0.0.0", port=7860, debug=False)
120
 
121
  if __name__ == "__main__":
122
- main()
123
-
124
- Client test (super simple):
125
- import requests
126
- API_URL = "https://corvo-ai-xx-mistral.hf.space/infer"
127
-
128
- payload = {
129
- "image_url": "https://images.unsplash.com/photo-1518791841217-8f162f1e1131",
130
- "messages": [
131
- {"role": "system", "content": "You are a helpful vision assistant."},
132
- {"role": "user", "content": "What do you see in this image?"}
133
- ]
134
- }
135
-
136
- r = requests.post(API_URL, json=payload, timeout=60)
137
- print(r.status_code)
138
- print(r.text)
 
3
  import requests
4
  from pathlib import Path
5
  from typing import Optional, List, Dict, Any
6
+ from flask import Flask, request, jsonify
7
 
8
  app = Flask(__name__)
9
 
 
24
  return base64.b64encode(resp.content).decode("utf-8")
25
 
26
  def normalize_messages_to_prompt(messages: List[Dict[str, str]]) -> str:
27
+ # Convert structured chat history into a single prompt string
28
+ # Expected roles: system, user, assistant
29
+ # Output example:
30
+ # System: ...
31
+ # User: ...
32
+ # Assistant: ...
33
  lines = []
34
  for msg in messages:
35
  role = str(msg.get("role", "")).strip().lower()
 
43
  else:
44
  prefix = "User"
45
  lines.append(f"{prefix}: {content}")
46
+ # Optionally add a final cue for the assistant to respond
47
  return "\n".join(lines).strip()
48
 
49
  def extract_output(obj: dict) -> str:
50
+ # Try common shapes:
51
+ # 1) { data: { outputs: [ { output: "..." } ] } }
52
  try:
53
  out = obj["data"]["outputs"][0]["output"]
54
  if isinstance(out, str):
55
  return out
56
  except Exception:
57
  pass
58
+ # 2) Flat { "output": "..." }
59
  if isinstance(obj, dict) and isinstance(obj.get("output"), str):
60
  return obj["output"]
61
+ # Fallback: empty
62
  return ""
63
 
64
+ def call_roboflow_playground(image_b64: str, prompt: str, model: str = DEFAULT_MODEL, timeout: int = 60) -> Dict[str, Any]:
65
  payload = {
66
  "inputs": {
67
  "image": {"type": "base64", "value": image_b64},
 
84
  @app.route("/infer", methods=["POST"])
85
  def infer():
86
  try:
87
+ data = request.get_json(force=True, silent=False) or {}
88
 
89
+ # Image sources: image_base64, image_url, or image_path (optional local)
90
  image_b64 = (data.get("image_base64") or "").strip()
91
  image_url = (data.get("image_url") or "").strip()
92
  image_path = (data.get("image_path") or "").strip()
93
 
94
  if not image_b64:
95
  if image_url:
96
+ try:
97
+ image_b64 = encode_image_to_base64_from_url(image_url)
98
+ except Exception as e:
99
+ return jsonify({"error": f"Failed to fetch/encode image_url: {str(e)}"}), 400
100
  elif image_path:
101
+ try:
102
+ image_b64 = encode_image_to_base64_from_path(image_path)
103
+ except Exception as e:
104
+ return jsonify({"error": f"Failed to read/encode image_path: {str(e)}"}), 400
105
  else:
106
+ return jsonify({"error": "Provide one of: image_base64, image_url, or image_path"}), 400
107
 
108
+ # Messages to prompt
109
  messages = data.get("messages", [])
110
+ if not isinstance(messages, list) or not all(isinstance(m, dict) for m in messages):
111
+ return jsonify({"error": "messages must be a list of {role, content}"}), 400
112
 
113
  prompt = normalize_messages_to_prompt(messages)
114
  if not prompt:
115
+ return jsonify({"error": "messages is empty or has no valid content"}), 400
116
 
117
+ # Model (optional override)
118
  model = (data.get("model") or DEFAULT_MODEL).strip() or DEFAULT_MODEL
119
 
120
+ # Call external API
121
+ raw_response = call_roboflow_playground(image_b64=image_b64, prompt=prompt, model=model)
122
 
123
+ # Extract simplified output
124
+ output_text = extract_output(raw_response)
125
 
126
+ return jsonify({
127
+ "output": output_text,
128
+ "raw_response": raw_response
129
+ }), 200
130
 
131
  except requests.HTTPError as e:
132
  status = getattr(e.response, "status_code", 502)
133
  text = getattr(e.response, "text", "")
134
+ return jsonify({"error": "HTTP error from upstream", "status": status, "response_text": text}), 502
135
  except Exception as e:
136
+ return jsonify({"error": str(e)}), 500
137
 
138
  def main():
139
+ # Run Flask on port 7860
140
  app.run(host="0.0.0.0", port=7860, debug=False)
141
 
142
  if __name__ == "__main__":
143
+ main()