TymaaHammouda's picture
Update app.py
8cd4d68 verified
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.responses import JSONResponse
import os
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from openai import OpenAI
print("Version ---- DeepSeek Only")
app = FastAPI()
# -----------------------------
# Request schema
# -----------------------------
class ConflictDetectionRequest(BaseModel):
Req1: str
Req2: str
model_choice: str # "GPT-4", "DeepSeek-Reasoner", "Fanar"
prompt_type: str # "zero-shot" or "few-shot"
api_key: str = None # required only if model_choice == "GPT-4"
# -----------------------------
# Prompt builder
# -----------------------------
def build_prompt(req1, req2, prompt_type="zero-shot"):
if prompt_type == "zero-shot":
return f"Do the following sentences contradict each other, Answer with only 'yes' or 'no', no explanation. \n 1.{req1} 2.{req2}"
elif prompt_type == "few-shot":
examples = (
"Example 1:\n"
"Req1: The system shall allow password reset.\n"
"Req2: The system shall not allow password reset.\n"
"Answer: yes\n\n"
"Example 2:\n"
"Req1: The system shall support Arabic language.\n"
"Req2: The system shall support English language.\n"
"Answer: no\n\n"
)
return examples + f"Now answer: Do the following sentences contradict each other? 1.{req1} 2.{req2}"
else:
return f"Do the following sentences contradict each other, yes or no: 1.{req1} 2.{req2}"
# -----------------------------
# Startup: load DeepSeek once
# -----------------------------
@app.on_event("startup")
def load_models():
print("Loading DeepSeek model into memory...")
model_name = "deepseek-ai/DeepSeek-R1-Distill-Llama-8B"
app.state.deepseek_tokenizer = AutoTokenizer.from_pretrained(model_name)
app.state.deepseek_tokenizer.pad_token = app.state.deepseek_tokenizer.eos_token
app.state.deepseek_model = AutoModelForCausalLM.from_pretrained(
model_name,
# max_tokens=10, # Limit to very few tokens
temperature=0.1, # Low temperature for more deterministic output
do_sample=False, # Disable sampling for consistent results
torch_dtype="auto",
device_map="auto",
offload_folder="offload"
)
# -----------------------------
# Model handlers
# -----------------------------
def run_gpt4(req1, req2, prompt_type, api_key):
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key)
prompt = build_prompt(req1, req2, prompt_type)
completion = client.chat.completions.create(
model="openai/gpt-4",
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=512
)
return completion.choices[0].message.content.strip()
def run_deepseek(req1, req2, prompt_type):
tokenizer = app.state.deepseek_tokenizer
model = app.state.deepseek_model
prompt = build_prompt(req1, req2, prompt_type)
inputs = tokenizer([prompt], return_tensors="pt", padding=True, truncation=True)
outputs = model.generate(
input_ids=inputs.input_ids,
attention_mask=inputs.attention_mask,
max_new_tokens=256,
pad_token_id=tokenizer.eos_token_id
)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
def run_fanar(req1, req2, prompt_type):
client = OpenAI(base_url="https://api.fanar.qa/v1", api_key=os.getenv("FANAR_API"))
prompt = build_prompt(req1, req2, prompt_type)
response = client.chat.completions.create(
model="Fanar",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content.strip()
# -----------------------------
# API route
# -----------------------------
@app.post("/predict")
def predict(request: ConflictDetectionRequest):
try:
if request.model_choice == "GPT-4":
if not request.api_key:
return JSONResponse({"error": "API key required for GPT-4"}, status_code=400)
answer = run_gpt4(request.Req1, request.Req2, request.prompt_type, request.api_key)
elif request.model_choice == "DeepSeek-Reasoner":
answer = run_deepseek(request.Req1, request.Req2, request.prompt_type)
elif request.model_choice == "Fanar":
answer = run_fanar(request.Req1, request.Req2, request.prompt_type)
else:
return JSONResponse({"error": "Invalid model_choice"}, status_code=400)
return JSONResponse({"resp": answer, "statusText": "OK", "statusCode": 0}, status_code=200)
except Exception as e:
return JSONResponse({"error": str(e)}, status_code=500)
def run_fanar_to_resolve(req1, req2):
client = OpenAI(base_url="https://api.fanar.qa/v1", api_key=os.getenv("FANAR_API"))
prompt = f"""
You are a requirements engineer. You are given two potentially conflicting requirements.
Requirement 1: "{req1}"
Requirement 2: "{req2}"
Your task is to resolve the conflict in Arabic using a hybrid strategy:
1. If possible, combine both requirements into **a single non-conflicting and concise requirement** that preserves the intent of both.
2. If they cannot be merged clearly, rewrite them as **two separate non-conflicting requirements**, possibly by adding conditions, default cases, or clarifying their scope.
Avoid using vague expressions such as 'as needed', 'if possible', or 'may'. Only merge requirements if they can be resolved clearly and deterministically. Otherwise, indicate that clarification is needed.
The resolved output must follow proper software requirement language and be easy to test.
Format your answer like this:
[Resolution Type]: Single / Split
Resolved Requirement(s):
- ...
"""
response = client.chat.completions.create(
model="Fanar",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content.strip()
class ConflictResoluionRequest(BaseModel):
Req1: str
Req2: str
@app.post("/resolve")
def resolve(request: ConflictResoluionRequest):
answer = run_fanar_to_resolve(request.Req1, request.Req2)
return JSONResponse({"resp": answer, "statusText": "OK", "statusCode": 0}, status_code=200)