adrienbrdne commited on
Commit
ad78361
·
verified ·
1 Parent(s): 748d181

Update api.py

Browse files
Files changed (1) hide show
  1. api.py +160 -4
api.py CHANGED
@@ -1,7 +1,10 @@
1
  import logging
2
  import time
3
  import uvicorn
4
- from fastapi import FastAPI, HTTPException
 
 
 
5
  from pydantic import BaseModel
6
  from contextlib import asynccontextmanager
7
  from typing import List, Dict, Any
@@ -20,6 +23,14 @@ except ImportError as e:
20
  # You might want to exit or raise a clearer error if imports fail
21
  raise
22
 
 
 
 
 
 
 
 
 
23
  # Configure logging for the API
24
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
25
  logger = logging.getLogger(__name__)
@@ -34,11 +45,25 @@ class KeyIssueResponse(BaseModel):
34
  """Response body containing the generated key issues."""
35
  key_issues: List[KigKeyIssue] # Use the KeyIssue schema from kig_core
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  # --- Global Variables / State ---
38
  # Keep the graph instance global for efficiency if desired,
39
  # but consider potential concurrency issues if graph/LLMs have state.
40
  # Rebuilding on each request is safer for statelessness.
41
  app_graph = None # Will be initialized at startup
 
42
 
43
  # --- Application Lifecycle (Startup/Shutdown) ---
44
  @asynccontextmanager
@@ -67,6 +92,25 @@ async def lifespan(app: FastAPI):
67
  # Decide if the app should fail to start
68
  raise RuntimeError("Failed to build LangGraph on startup.") from e
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  yield # API runs here
71
 
72
  # --- Shutdown ---
@@ -79,9 +123,9 @@ async def lifespan(app: FastAPI):
79
 
80
  # --- FastAPI Application ---
81
  app = FastAPI(
82
- title="Key Issue Generator API",
83
- description="API to generate Key Issues based on a technical query using LLMs and Neo4j.",
84
- version="1.0.0",
85
  lifespan=lifespan # Use the lifespan context manager
86
  )
87
 
@@ -191,6 +235,118 @@ async def generate_issues(request: KeyIssueRequest):
191
  raise HTTPException(status_code=500, detail=f"Internal Server Error: An unexpected error occurred.")
192
 
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  # --- How to Run ---
195
  if __name__ == "__main__":
196
  # Make sure to set environment variables for config (NEO4J_URI, NEO4J_PASSWORD, GEMINI_API_KEY, etc.)
 
1
  import logging
2
  import time
3
  import uvicorn
4
+ import requests
5
+ import os # Added import for environment variables
6
+
7
+ from fastapi import FastAPI, HTTPException, Body
8
  from pydantic import BaseModel
9
  from contextlib import asynccontextmanager
10
  from typing import List, Dict, Any
 
23
  # You might want to exit or raise a clearer error if imports fail
24
  raise
25
 
26
+ # Added imports for Gemini
27
+ try:
28
+ import google.generativeai as genai
29
+ from google.generativeai.types import GenerationConfig, Content, Part # Corrected import path
30
+ except ImportError:
31
+ print("google.generativeai library not found. Please install it: pip install google-generativeai")
32
+ genai = None # Set to None if import fails
33
+
34
  # Configure logging for the API
35
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
36
  logger = logging.getLogger(__name__)
 
45
  """Response body containing the generated key issues."""
46
  key_issues: List[KigKeyIssue] # Use the KeyIssue schema from kig_core
47
 
48
+ class SpecificityEvaluationRequest(BaseModel):
49
+ title: str
50
+ description: str
51
+ technical_topic: str
52
+
53
+ class SpecificityScore(BaseModel):
54
+ predicted_class: str
55
+ score: float
56
+
57
+ class SpecificityEvaluationResponse(BaseModel):
58
+ problematic: str
59
+ specificity: SpecificityScore
60
+
61
  # --- Global Variables / State ---
62
  # Keep the graph instance global for efficiency if desired,
63
  # but consider potential concurrency issues if graph/LLMs have state.
64
  # Rebuilding on each request is safer for statelessness.
65
  app_graph = None # Will be initialized at startup
66
+ gemini_client = None # Will be initialized at startup
67
 
68
  # --- Application Lifecycle (Startup/Shutdown) ---
69
  @asynccontextmanager
 
92
  # Decide if the app should fail to start
93
  raise RuntimeError("Failed to build LangGraph on startup.") from e
94
 
95
+ # Initialize Gemini Client
96
+ logger.info("Initializing Gemini client...")
97
+ if genai:
98
+ try:
99
+ # Assuming GEMINI_API_KEY is set in environment or loaded via settings
100
+ api_key = os.getenv("GEMINI_API_KEY") or getattr(settings, "GEMINI_API_KEY", None)
101
+ if not api_key:
102
+ raise ValueError("GEMINI_API_KEY not found in environment or settings.")
103
+ genai.configure(api_key=api_key)
104
+ # Optionally, you could create a specific model instance here if needed frequently
105
+ # gemini_client = genai.GenerativeModel(...)
106
+ logger.info("Gemini client configured successfully.")
107
+ except Exception as e:
108
+ logger.error(f"Failed to configure Gemini client: {e}", exc_info=True)
109
+ # Decide if the app should fail to start or just log the error
110
+ # gemini_client will remain None, endpoints using it will fail
111
+ else:
112
+ logger.warning("Gemini library not imported. Endpoints requiring Gemini will not work.")
113
+
114
  yield # API runs here
115
 
116
  # --- Shutdown ---
 
123
 
124
  # --- FastAPI Application ---
125
  app = FastAPI(
126
+ title="Key Issue Generator Specificity API",
127
+ description="API to generate Key Issues based on a technical query using LLMs and Neo4j and evaluate problematic specificity.",
128
+ version="1.1.0",
129
  lifespan=lifespan # Use the lifespan context manager
130
  )
131
 
 
235
  raise HTTPException(status_code=500, detail=f"Internal Server Error: An unexpected error occurred.")
236
 
237
 
238
+ @app.post("/evaluate-specificity", response_model=SpecificityEvaluationResponse)
239
+ async def evaluation(request: SpecificityEvaluationRequest):
240
+ """
241
+ Generates a technical problematic using Gemini based on title, description,
242
+ and topic, then evaluates its specificity using an external API (fine-tuned model for specificity).
243
+ """
244
+ # Check if Gemini library was imported and configured
245
+ if not genai or not genai.is_configured():
246
+ logger.error("Gemini client is not available or configured.")
247
+ raise HTTPException(status_code=503, detail="Service Unavailable: Gemini client not configured")
248
+
249
+ title = request.title
250
+ description = request.description
251
+ technical_topic = request.technical_topic
252
+
253
+ if not all([title, description, technical_topic]):
254
+ raise HTTPException(status_code=400, detail="Missing required fields: title, description, or technical_topic.")
255
+
256
+ logger.info("Received request for specificity evaluation.")
257
+ logger.debug(f"Title: {title}, Topic: {technical_topic}") # Avoid logging full description unless needed
258
+
259
+ # --- 1. Generate Problematic using Gemini ---
260
+ prompt = f"""I want you to create a technical problematic using a key issue composed of a title and a detailed description.
261
+ Here is the title of the key issue to deal with: <title>{title}</title>
262
+
263
+ And here is the associated description: <description>{description}</description>
264
+
265
+ This key issue is part of the following technical topic: <technical_topic>{technical_topic}</technical_topic>
266
+
267
+ The problematic must be in interrogative form.
268
+ As the output, I only want you to provide the problematic found, nothing else.
269
+
270
+ Here are examples of problematics that you could create, it shows the level of specificity that you should aim for:
271
+
272
+ Example 1: 'How can a method for allocating radio resources in a non-GSO satellite communication system, operating in frequency bands shared with geostationary satellite systems and particularly in high frequency bands such as Ka, minimize interference to geostationary systems, without causing reduced geographic coverage due to fixed high separation angle thresholds or incurring cost and sub-optimality from over-dimensioning the non-GSO satellite constellation?'
273
+ Example 2: 'How to address the vulnerability of on-aircraft avionics software update integrity checks to system compromises and the logistical challenges of cryptographic key management in digital signature solutions, in order to establish a more secure and logistically efficient method for updating safety-critical avionics equipment?'
274
+ Example 3: 'How can SIM cards be protected against collision attacks that aim to retrieve the secret key Ki by analyzing the input and output of the authentication algorithm during the standard GSM authentication process, given that current tamper-proof measures are insufficient to prevent this type of key extraction?'
275
+ Example 4: 'How can a Trusted Application in a GlobalPlatform compliant TEE overcome the GP specification limitations that enforce client blocking during task execution, prevent partial task execution, and delete TA execution context between commands, to function as a persistent server with stateful sessions and asynchronous communication capabilities, thereby enabling server functionalities like continuous listening and non-blocking send/receive, currently impossible due to GP's sequential task processing and stateless TA operation?'
276
+
277
+ As far as possible, avoid using acronyms in the problematic.
278
+ Try to be about the same length as the examples if possible."""
279
+
280
+ try:
281
+ logger.info("Calling Gemini API to generate problematic...")
282
+ # Use the specified model and configuration
283
+ model_name = "gemini-1.5-flash-latest" # Changed to a generally available model
284
+ model = genai.GenerativeModel(model_name)
285
+
286
+ # Prepare contents (ensure correct structure for the library version)
287
+ contents = [Content(role="user", parts=[Part.from_text(text=prompt)])]
288
+
289
+ # Define generation config
290
+ generate_config = GenerationConfig(response_mime_type="text/plain") # Updated parameter name
291
+
292
+ # Make the API call
293
+ response = await model.generate_content_async(
294
+ contents=contents,
295
+ generation_config=generate_config # Pass config object
296
+ )
297
+
298
+ # Extract the result
299
+ problematic_result = response.text.strip()
300
+ logger.info("Successfully generated problematic from Gemini.")
301
+ logger.debug(f"Generated problematic: {problematic_result[:200]}...") # Log snippet
302
+
303
+ except Exception as e:
304
+ logger.error(f"Error calling Gemini API: {e}", exc_info=True)
305
+ # Check for specific Gemini API errors if possible
306
+ raise HTTPException(status_code=502, detail=f"Failed to generate problematic using LLM: {e}")
307
+
308
+ if not problematic_result:
309
+ logger.error("Gemini API returned an empty result.")
310
+ raise HTTPException(status_code=502, detail="LLM returned an empty problematic.")
311
+
312
+ # --- 2. Evaluate Specificity using External API ---
313
+ API_URL = "https://organizedprogrammers-fastapi-problematic-specificity.hf.space"
314
+ endpoint = f"{API_URL}/predict"
315
+ data = {"text": problematic_result}
316
+
317
+ try:
318
+ logger.info(f"Calling specificity prediction API at {endpoint}...")
319
+ prediction_response = requests.post(endpoint, json=data, timeout=30) # Added timeout
320
+ prediction_response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
321
+
322
+ score_data = prediction_response.json()
323
+ logger.info(f"Successfully received specificity score: {score_data}")
324
+
325
+ # Validate the received score data against Pydantic model
326
+ try:
327
+ specificity_score = SpecificityScore(**score_data)
328
+ except Exception as pydantic_error: # Catch validation errors
329
+ logger.error(f"Failed to validate specificity score response: {pydantic_error}", exc_info=True)
330
+ logger.error(f"Invalid data received from specificity API: {score_data}")
331
+ raise HTTPException(status_code=502, detail="Invalid response format from specificity prediction API.")
332
+
333
+ except requests.exceptions.RequestException as e:
334
+ logger.error(f"Error calling specificity prediction API: {e}", exc_info=True)
335
+ raise HTTPException(status_code=502, detail=f"Failed to call specificity prediction API: {e}")
336
+ except Exception as e: # Catch other potential errors like JSON decoding
337
+ logger.error(f"Unexpected error during specificity evaluation: {e}", exc_info=True)
338
+ raise HTTPException(status_code=500, detail=f"Internal error during specificity evaluation: {e}")
339
+
340
+
341
+ # --- 3. Return Combined Result ---
342
+ final_response = SpecificityEvaluationResponse(
343
+ problematic=problematic_result,
344
+ specificity=specificity_score
345
+ )
346
+
347
+ return final_response
348
+
349
+
350
  # --- How to Run ---
351
  if __name__ == "__main__":
352
  # Make sure to set environment variables for config (NEO4J_URI, NEO4J_PASSWORD, GEMINI_API_KEY, etc.)