Nhughes09 commited on
Commit
e54f4cc
·
1 Parent(s): 46f546d

Modular architecture: logging_config.py, cloudflare_client.py, app.py for Cloudflare AI

Browse files
Files changed (4) hide show
  1. __pycache__/app.cpython-314.pyc +0 -0
  2. app.py +94 -118
  3. cloudflare_client.py +205 -0
  4. logging_config.py +33 -0
__pycache__/app.cpython-314.pyc DELETED
Binary file (9.63 kB)
 
app.py CHANGED
@@ -1,157 +1,133 @@
 
1
  import gradio as gr
2
- import requests
3
- import logging
4
- import sys
5
- import os
6
  import json
7
- from datetime import datetime
8
 
9
  # ============================================================================
10
- # LOGGING SETUP
11
  # ============================================================================
12
- logging.basicConfig(
13
- level=logging.DEBUG,
14
- format="%(asctime)s | %(levelname)-8s | %(message)s",
15
- handlers=[logging.StreamHandler(sys.stdout)]
16
- )
17
- logger = logging.getLogger("ChatbotBrain")
18
-
19
- def log_banner(text):
20
- logger.info("=" * 60)
21
- logger.info(f" {text}")
22
- logger.info("=" * 60)
23
-
24
- log_banner("CPU CHATBOT - CLOUDFLARE AI BACKEND")
25
- logger.info(f"Start Time: {datetime.now().isoformat()}")
26
- logger.info(f"Python: {sys.version}")
27
- logger.info(f"Gradio: {gr.__version__}")
28
 
29
  # ============================================================================
30
- # CLOUDFLARE API CONFIGURATION
31
  # ============================================================================
32
- # Your Cloudflare Worker endpoint
33
- CLOUDFLARE_ENDPOINT = "https://cloudflare.nlhughes08.workers.dev"
34
 
35
- logger.info(f"Backend: {CLOUDFLARE_ENDPOINT}")
 
 
 
 
 
 
 
 
 
36
 
37
  # ============================================================================
38
- # API CALL FUNCTION
39
  # ============================================================================
40
- def call_cloudflare_ai(message, history):
41
- """Call the Cloudflare Workers AI backend."""
42
- logger.info("-" * 40)
43
- logger.info(f"USER MESSAGE: {message}")
44
- logger.info(f"History length: {len(history)}")
45
 
46
- # Build messages array
47
- messages = []
48
- for user_msg, bot_msg in history:
49
- messages.append({"role": "user", "content": user_msg})
50
- if bot_msg:
51
- messages.append({"role": "assistant", "content": bot_msg})
52
- messages.append({"role": "user", "content": message})
53
 
54
- # Try multiple endpoint patterns
55
- endpoints_to_try = [
56
- (f"{CLOUDFLARE_ENDPOINT}/api/chat", {"messages": messages}),
57
- (f"{CLOUDFLARE_ENDPOINT}/chat", {"messages": messages}),
58
- (f"{CLOUDFLARE_ENDPOINT}/api/chat", {"message": message, "history": history}),
59
- (f"{CLOUDFLARE_ENDPOINT}", {"messages": messages}),
60
- ]
61
 
62
- for url, payload in endpoints_to_try:
63
- logger.info(f"Trying: {url}")
64
- logger.debug(f"Payload: {json.dumps(payload)[:200]}")
65
-
66
- try:
67
- response = requests.post(
68
- url,
69
- headers={"Content-Type": "application/json"},
70
- json=payload,
71
- timeout=60
72
- )
73
-
74
- logger.info(f"Status: {response.status_code}")
75
-
76
- if response.status_code == 200:
77
- try:
78
- result = response.json()
79
- logger.info(f"Response: {json.dumps(result)[:200]}")
80
-
81
- # Try different response formats
82
- if isinstance(result, dict):
83
- text = (
84
- result.get("response") or
85
- result.get("message") or
86
- result.get("content") or
87
- result.get("choices", [{}])[0].get("message", {}).get("content") or
88
- str(result)
89
- )
90
- else:
91
- text = str(result)
92
-
93
- if text:
94
- logger.info(f"SUCCESS: Got response from {url}")
95
- return text
96
- except:
97
- text = response.text
98
- if text:
99
- logger.info(f"SUCCESS (text): Got response from {url}")
100
- return text
101
-
102
- logger.warning(f"Failed: {response.status_code} - {response.text[:100]}")
103
-
104
- except Exception as e:
105
- logger.error(f"Error calling {url}: {e}")
106
- continue
107
 
108
- # If all endpoints fail, return error with helpful info
109
- error_msg = f"""Sorry, I could not connect to the AI backend.
110
-
111
- Tried endpoints:
112
- - {CLOUDFLARE_ENDPOINT}/api/chat
113
- - {CLOUDFLARE_ENDPOINT}/chat
114
- - {CLOUDFLARE_ENDPOINT}
115
-
116
- You can use the AI directly at:
117
- {CLOUDFLARE_ENDPOINT}
118
-
119
- Please check the Container Logs for details."""
120
 
121
- return error_msg
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  # ============================================================================
124
  # GRADIO UI
125
  # ============================================================================
126
- log_banner("BUILDING GRADIO UI")
127
 
128
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
129
  gr.Markdown("# CPU Chatbot")
130
- gr.Markdown("### Powered by Cloudflare Workers AI")
131
- gr.Markdown(f"Backend: [{CLOUDFLARE_ENDPOINT}]({CLOUDFLARE_ENDPOINT})")
132
 
133
- chatbot = gr.Chatbot(height=500)
134
- msg = gr.Textbox(placeholder="Ask me anything...", label="Your message")
135
- clear = gr.ClearButton([msg, chatbot])
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
  def user_submit(message, history):
138
  if not message.strip():
139
- return "", history
140
- return "", history + [[message, None]]
141
 
142
  def bot_respond(history):
143
  if not history:
144
- return history
145
  user_message = history[-1][0]
146
- bot_response = call_cloudflare_ai(user_message, history[:-1])
147
  history[-1][1] = bot_response
148
- return history
149
 
150
- msg.submit(user_submit, [msg, chatbot], [msg, chatbot], queue=False).then(
151
- bot_respond, chatbot, chatbot
 
 
 
 
 
 
152
  )
 
153
 
154
- log_banner("SYSTEM READY")
155
 
156
  if __name__ == "__main__":
157
  demo.launch()
 
1
+ # app.py - Main Gradio Application
2
  import gradio as gr
3
+ from logging_config import setup_logging, log_banner, log_section, log_startup_info
4
+ from cloudflare_client import CloudflareAIClient
 
 
5
  import json
 
6
 
7
  # ============================================================================
8
+ # INITIALIZATION
9
  # ============================================================================
10
+ logger = setup_logging()
11
+ log_startup_info(logger)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  # ============================================================================
14
+ # CLOUDFLARE CONFIGURATION
15
  # ============================================================================
16
+ # Your Cloudflare Worker endpoint - update this when deployed!
17
+ CLOUDFLARE_ENDPOINT = "https://cloudflarellamaworker.nlhughes08.workers.dev"
18
 
19
+ log_section(logger, "CLOUDFLARE AI CLIENT SETUP")
20
+ ai_client = CloudflareAIClient(logger, CLOUDFLARE_ENDPOINT)
21
+
22
+ # Test connection on startup
23
+ logger.info("Testing Cloudflare endpoint on startup...")
24
+ connection_ok = ai_client.test_connection()
25
+ if connection_ok:
26
+ logger.info("Cloudflare endpoint is REACHABLE")
27
+ else:
28
+ logger.warning("Cloudflare endpoint may not be ready - check deployment")
29
 
30
  # ============================================================================
31
+ # CHAT RESPONSE FUNCTION
32
  # ============================================================================
33
+ def respond(message, history):
34
+ """Generate AI response using Cloudflare Workers AI."""
35
+ log_section(logger, "NEW USER MESSAGE")
36
+ logger.info(f"User: {message}")
37
+ logger.info(f"History: {len(history)} previous messages")
38
 
39
+ # Build context from history (simple approach)
40
+ context = ""
41
+ if history:
42
+ for user_msg, bot_msg in history[-5:]: # Last 5 exchanges
43
+ context += f"User: {user_msg}\n"
44
+ if bot_msg:
45
+ context += f"Assistant: {bot_msg}\n"
46
 
47
+ # Full prompt with context
48
+ if context:
49
+ full_prompt = f"{context}User: {message}\nAssistant:"
50
+ else:
51
+ full_prompt = message
 
 
52
 
53
+ logger.debug(f"Full prompt length: {len(full_prompt)} chars")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ # Call Cloudflare AI
56
+ result = ai_client.call_ai(full_prompt)
 
 
 
 
 
 
 
 
 
 
57
 
58
+ if result["success"]:
59
+ response_text = result["response"]
60
+ # Clean up any model artifacts like <|start_header_id|>
61
+ for tag in ["<|start_header_id|>", "<|end_header_id|>", "<|eot_id|>"]:
62
+ response_text = response_text.replace(tag, "")
63
+ return response_text.strip()
64
+ else:
65
+ # Return error with diagnostic info
66
+ error_msg = result.get("error", "Unknown error")
67
+ diagnosis = result.get("diagnosis", {})
68
+
69
+ error_response = f"Error: {error_msg}\n\n"
70
+
71
+ if diagnosis.get("suggestions"):
72
+ error_response += "Troubleshooting:\n"
73
+ for suggestion in diagnosis["suggestions"]:
74
+ error_response += f"• {suggestion}\n"
75
+
76
+ error_response += f"\nEndpoint: {CLOUDFLARE_ENDPOINT}"
77
+
78
+ return error_response
79
 
80
  # ============================================================================
81
  # GRADIO UI
82
  # ============================================================================
83
+ log_section(logger, "BUILDING GRADIO UI")
84
 
85
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
86
  gr.Markdown("# CPU Chatbot")
87
+ gr.Markdown("### Powered by Cloudflare Workers AI (Llama Guard 3)")
88
+ gr.Markdown(f"**Endpoint:** `{CLOUDFLARE_ENDPOINT}`")
89
 
90
+ with gr.Row():
91
+ with gr.Column(scale=4):
92
+ chatbot = gr.Chatbot(height=500, label="Chat")
93
+ msg = gr.Textbox(placeholder="Ask me anything...", label="Your message")
94
+ with gr.Row():
95
+ submit_btn = gr.Button("Send", variant="primary")
96
+ clear = gr.ClearButton([msg, chatbot], value="Clear")
97
+
98
+ with gr.Column(scale=1):
99
+ gr.Markdown("### Status")
100
+ status_box = gr.JSON(
101
+ label="Client Stats",
102
+ value=ai_client.get_stats()
103
+ )
104
+ refresh_btn = gr.Button("Refresh Stats")
105
 
106
  def user_submit(message, history):
107
  if not message.strip():
108
+ return "", history, ai_client.get_stats()
109
+ return "", history + [[message, None]], ai_client.get_stats()
110
 
111
  def bot_respond(history):
112
  if not history:
113
+ return history, ai_client.get_stats()
114
  user_message = history[-1][0]
115
+ bot_response = respond(user_message, history[:-1])
116
  history[-1][1] = bot_response
117
+ return history, ai_client.get_stats()
118
 
119
+ def refresh_stats():
120
+ return ai_client.get_stats()
121
+
122
+ msg.submit(user_submit, [msg, chatbot], [msg, chatbot, status_box], queue=False).then(
123
+ bot_respond, chatbot, [chatbot, status_box]
124
+ )
125
+ submit_btn.click(user_submit, [msg, chatbot], [msg, chatbot, status_box], queue=False).then(
126
+ bot_respond, chatbot, [chatbot, status_box]
127
  )
128
+ refresh_btn.click(refresh_stats, outputs=status_box)
129
 
130
+ log_banner(logger, "SYSTEM READY - WAITING FOR MESSAGES")
131
 
132
  if __name__ == "__main__":
133
  demo.launch()
cloudflare_client.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # cloudflare_client.py - Cloudflare Workers AI API Client
2
+ import requests
3
+ import json
4
+ from datetime import datetime
5
+
6
+ class CloudflareAIClient:
7
+ """Client for calling Cloudflare Workers AI endpoints."""
8
+
9
+ def __init__(self, logger, endpoint_url):
10
+ self.logger = logger
11
+ self.endpoint_url = endpoint_url
12
+ self.request_count = 0
13
+ self.success_count = 0
14
+ self.error_count = 0
15
+ self.last_error = None
16
+
17
+ self.logger.info(f"CloudflareAIClient initialized")
18
+ self.logger.info(f"Endpoint: {endpoint_url}")
19
+
20
+ def get_stats(self):
21
+ """Return client statistics."""
22
+ return {
23
+ "requests": self.request_count,
24
+ "successes": self.success_count,
25
+ "errors": self.error_count,
26
+ "last_error": self.last_error
27
+ }
28
+
29
+ def test_connection(self):
30
+ """Test if the endpoint is reachable."""
31
+ self.logger.info("Testing connection to Cloudflare endpoint...")
32
+ try:
33
+ response = requests.get(self.endpoint_url, timeout=10)
34
+ self.logger.info(f"Connection test: Status {response.status_code}")
35
+ self.logger.debug(f"Response: {response.text[:200]}")
36
+ return response.status_code in [200, 405] # 405 = method not allowed but reachable
37
+ except Exception as e:
38
+ self.logger.error(f"Connection test failed: {e}")
39
+ return False
40
+
41
+ def call_ai(self, prompt):
42
+ """
43
+ Call the Cloudflare Workers AI with a prompt.
44
+
45
+ Args:
46
+ prompt: The user's message/prompt
47
+
48
+ Returns:
49
+ dict with 'success', 'response' or 'error', and 'debug_info'
50
+ """
51
+ self.request_count += 1
52
+ request_id = f"REQ-{self.request_count:04d}"
53
+
54
+ self.logger.info("-" * 50)
55
+ self.logger.info(f"[{request_id}] NEW AI REQUEST")
56
+ self.logger.info(f"[{request_id}] Prompt: {prompt[:100]}...")
57
+ self.logger.info(f"[{request_id}] Endpoint: {self.endpoint_url}")
58
+
59
+ debug_info = {
60
+ "request_id": request_id,
61
+ "timestamp": datetime.now().isoformat(),
62
+ "endpoint": self.endpoint_url,
63
+ "prompt_length": len(prompt)
64
+ }
65
+
66
+ payload = {"prompt": prompt}
67
+ self.logger.debug(f"[{request_id}] Payload: {json.dumps(payload)}")
68
+
69
+ try:
70
+ self.logger.info(f"[{request_id}] Sending POST request...")
71
+ response = requests.post(
72
+ self.endpoint_url,
73
+ headers={"Content-Type": "application/json"},
74
+ json=payload,
75
+ timeout=60
76
+ )
77
+
78
+ debug_info["status_code"] = response.status_code
79
+ debug_info["response_time_estimate"] = "< 60s"
80
+
81
+ self.logger.info(f"[{request_id}] Response Status: {response.status_code}")
82
+ self.logger.debug(f"[{request_id}] Response Headers: {dict(response.headers)}")
83
+
84
+ if response.status_code == 200:
85
+ try:
86
+ result = response.json()
87
+ self.logger.info(f"[{request_id}] SUCCESS: Got JSON response")
88
+ self.logger.debug(f"[{request_id}] Response: {json.dumps(result)[:300]}")
89
+
90
+ # Extract the reply from the response
91
+ reply = result.get("reply") or result.get("response") or result.get("message")
92
+
93
+ if reply:
94
+ self.success_count += 1
95
+ self.logger.info(f"[{request_id}] AI Reply: {reply[:100]}...")
96
+ return {
97
+ "success": True,
98
+ "response": reply,
99
+ "debug_info": debug_info
100
+ }
101
+ else:
102
+ self.logger.warning(f"[{request_id}] No reply field in response")
103
+ return {
104
+ "success": True,
105
+ "response": str(result),
106
+ "debug_info": debug_info
107
+ }
108
+
109
+ except json.JSONDecodeError as e:
110
+ self.logger.error(f"[{request_id}] JSON parse error: {e}")
111
+ debug_info["error"] = f"JSON parse error: {e}"
112
+ return {
113
+ "success": False,
114
+ "error": f"Invalid JSON response: {response.text[:200]}",
115
+ "debug_info": debug_info
116
+ }
117
+ else:
118
+ self.error_count += 1
119
+ error_msg = f"HTTP {response.status_code}: {response.text[:200]}"
120
+ self.last_error = error_msg
121
+ self.logger.error(f"[{request_id}] API ERROR: {error_msg}")
122
+ debug_info["error"] = error_msg
123
+
124
+ # Provide helpful diagnostics based on error code
125
+ diagnosis = self._diagnose_error(response.status_code, response.text)
126
+ debug_info["diagnosis"] = diagnosis
127
+
128
+ return {
129
+ "success": False,
130
+ "error": error_msg,
131
+ "diagnosis": diagnosis,
132
+ "debug_info": debug_info
133
+ }
134
+
135
+ except requests.exceptions.Timeout:
136
+ self.error_count += 1
137
+ self.last_error = "Request timeout"
138
+ self.logger.error(f"[{request_id}] TIMEOUT after 60 seconds")
139
+ return {
140
+ "success": False,
141
+ "error": "Request timed out after 60 seconds",
142
+ "debug_info": debug_info
143
+ }
144
+ except requests.exceptions.ConnectionError as e:
145
+ self.error_count += 1
146
+ self.last_error = str(e)
147
+ self.logger.error(f"[{request_id}] CONNECTION ERROR: {e}")
148
+ return {
149
+ "success": False,
150
+ "error": f"Connection error: {e}",
151
+ "debug_info": debug_info
152
+ }
153
+ except Exception as e:
154
+ self.error_count += 1
155
+ self.last_error = str(e)
156
+ self.logger.error(f"[{request_id}] UNEXPECTED ERROR: {e}")
157
+ return {
158
+ "success": False,
159
+ "error": str(e),
160
+ "debug_info": debug_info
161
+ }
162
+
163
+ def _diagnose_error(self, status_code, response_text):
164
+ """Diagnose common errors and provide helpful suggestions."""
165
+ diagnosis = {"code": status_code, "suggestions": []}
166
+
167
+ if "1042" in response_text:
168
+ diagnosis["issue"] = "WORKER_NOT_DEPLOYED"
169
+ diagnosis["suggestions"] = [
170
+ "The Cloudflare Worker is not deployed yet",
171
+ "Push the worker code to GitHub to trigger deployment",
172
+ "Check the Cloudflare dashboard for build errors"
173
+ ]
174
+ elif status_code == 400:
175
+ diagnosis["issue"] = "BAD_REQUEST"
176
+ diagnosis["suggestions"] = [
177
+ "Check the request payload format",
178
+ "Ensure 'prompt' field is present"
179
+ ]
180
+ elif status_code == 401 or status_code == 403:
181
+ diagnosis["issue"] = "AUTHENTICATION_ERROR"
182
+ diagnosis["suggestions"] = [
183
+ "Check Cloudflare API token",
184
+ "Verify Worker permissions"
185
+ ]
186
+ elif status_code == 404:
187
+ diagnosis["issue"] = "NOT_FOUND"
188
+ diagnosis["suggestions"] = [
189
+ "Worker endpoint does not exist",
190
+ "Check the URL is correct"
191
+ ]
192
+ elif status_code == 500:
193
+ diagnosis["issue"] = "SERVER_ERROR"
194
+ diagnosis["suggestions"] = [
195
+ "Check Worker logs in Cloudflare dashboard",
196
+ "Verify AI binding is configured"
197
+ ]
198
+ else:
199
+ diagnosis["issue"] = f"UNKNOWN_ERROR_{status_code}"
200
+ diagnosis["suggestions"] = ["Check Cloudflare dashboard for details"]
201
+
202
+ for suggestion in diagnosis["suggestions"]:
203
+ self.logger.warning(f"SUGGESTION: {suggestion}")
204
+
205
+ return diagnosis
logging_config.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # logging_config.py - Centralized logging configuration
2
+ import logging
3
+ import sys
4
+ from datetime import datetime
5
+
6
+ def setup_logging():
7
+ """Configure and return the application logger."""
8
+ logging.basicConfig(
9
+ level=logging.DEBUG,
10
+ format="%(asctime)s | %(levelname)-8s | [%(module)s.%(funcName)s] %(message)s",
11
+ handlers=[logging.StreamHandler(sys.stdout)]
12
+ )
13
+ return logging.getLogger("ChatbotBrain")
14
+
15
+ def log_banner(logger, text):
16
+ """Log a prominent banner message."""
17
+ logger.info("=" * 70)
18
+ logger.info(f" {text}")
19
+ logger.info("=" * 70)
20
+
21
+ def log_section(logger, text):
22
+ """Log a section header."""
23
+ logger.info("-" * 50)
24
+ logger.info(f" >> {text}")
25
+ logger.info("-" * 50)
26
+
27
+ def log_startup_info(logger):
28
+ """Log startup information."""
29
+ import gradio as gr
30
+ log_banner(logger, "CPU CHATBOT - CLOUDFLARE AI BACKEND")
31
+ logger.info(f"Timestamp: {datetime.now().isoformat()}")
32
+ logger.info(f"Python: {sys.version}")
33
+ logger.info(f"Gradio: {gr.__version__}")