Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -56,21 +56,19 @@ app = FastAPI()
|
|
56 |
|
57 |
# --- Tool Execution Functions ---
|
58 |
def execute_browse_tool(url: str) -> str:
|
59 |
-
"""Visits a URL, extracts text content, and returns it."""
|
60 |
try:
|
61 |
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
|
62 |
response = requests.get(url, headers=headers, timeout=10)
|
63 |
response.raise_for_status()
|
64 |
soup = BeautifulSoup(response.content, 'html.parser')
|
65 |
-
# Remove script and style elements
|
66 |
for script in soup(["script", "style"]):
|
67 |
script.decompose()
|
68 |
text = soup.get_text(separator='\n', strip=True)
|
69 |
-
return f"Content from {url}:\n\n{text[:4000]}"
|
70 |
except Exception as e:
|
71 |
return f"Error browsing {url}: {str(e)}"
|
72 |
|
73 |
-
# --- Pydantic Models ---
|
74 |
class ContentPart(BaseModel): type: str; text: str
|
75 |
class ChatMessage(BaseModel): role: str; content: Union[str, List[ContentPart]]
|
76 |
class ChatCompletionRequest(BaseModel):
|
@@ -97,30 +95,27 @@ async def create_chat_completion(request: ChatCompletionRequest):
|
|
97 |
|
98 |
if not user_prompt: return {"error": "Prompt not found."}
|
99 |
|
100 |
-
# --- AGENT LOGIC ---
|
101 |
-
# STEP 1: Ask the model to think and decide on a tool
|
102 |
initial_messages = [{'role': 'system', 'content': SYSTEM_PROMPT}, {'role': 'user', 'content': user_prompt}]
|
103 |
inputs = tokenizer.apply_chat_template(initial_messages, add_generation_prompt=True, return_tensors="pt").to(DEVICE)
|
104 |
-
|
105 |
-
|
|
|
|
|
106 |
|
107 |
tool_call = None
|
108 |
try:
|
109 |
-
# Check if the model's output is a JSON for a tool call
|
110 |
json_part = thought_process[thought_process.find('{'):thought_process.rfind('}')+1]
|
111 |
if json_part:
|
112 |
tool_call = json.loads(json_part)
|
113 |
except json.JSONDecodeError:
|
114 |
tool_call = None
|
115 |
|
116 |
-
# STEP 2: Execute the tool if requested
|
117 |
if tool_call and 'tool' in tool_call:
|
118 |
tool_context = ""
|
119 |
if tool_call['tool'] == 'browse' and 'url' in tool_call:
|
120 |
print(f"--- AGENT: Browsing URL: {tool_call['url']} ---")
|
121 |
tool_context = execute_browse_tool(tool_call['url'])
|
122 |
|
123 |
-
# STEP 3: Call the model AGAIN with the new context
|
124 |
final_messages = [
|
125 |
{'role': 'system', 'content': SYSTEM_PROMPT},
|
126 |
{'role': 'user', 'content': user_prompt},
|
@@ -128,21 +123,20 @@ async def create_chat_completion(request: ChatCompletionRequest):
|
|
128 |
{'role': 'system', 'content': "Now, provide the final, complete answer to the user based on this information."}
|
129 |
]
|
130 |
else:
|
131 |
-
# If no tool is needed, just use the original thought process
|
132 |
final_messages = [
|
133 |
{'role': 'system', 'content': SYSTEM_PROMPT},
|
134 |
{'role': 'user', 'content': user_prompt},
|
135 |
-
{'role': 'assistant', 'content': thought_process}
|
136 |
]
|
137 |
|
138 |
-
# --- FINAL RESPONSE GENERATION (Streaming) ---
|
139 |
final_inputs = tokenizer.apply_chat_template(final_messages, add_generation_prompt=False, return_tensors="pt").to(DEVICE)
|
140 |
-
|
141 |
-
|
|
|
|
|
142 |
|
143 |
async def stream_generator():
|
144 |
response_id = f"chatcmpl-{uuid.uuid4()}"
|
145 |
-
# (Streaming logic is the same as before)
|
146 |
for char in response_text:
|
147 |
chunk = {"id": response_id, "object": "chat.completion.chunk", "created": int(time.time()), "model": MODEL_ID, "choices": [{"index": 0, "delta": {"content": char}, "finish_reason": None}]}
|
148 |
yield f"data: {json.dumps(chunk)}\n\n"
|
|
|
56 |
|
57 |
# --- Tool Execution Functions ---
|
58 |
def execute_browse_tool(url: str) -> str:
|
|
|
59 |
try:
|
60 |
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
|
61 |
response = requests.get(url, headers=headers, timeout=10)
|
62 |
response.raise_for_status()
|
63 |
soup = BeautifulSoup(response.content, 'html.parser')
|
|
|
64 |
for script in soup(["script", "style"]):
|
65 |
script.decompose()
|
66 |
text = soup.get_text(separator='\n', strip=True)
|
67 |
+
return f"Content from {url}:\n\n{text[:4000]}"
|
68 |
except Exception as e:
|
69 |
return f"Error browsing {url}: {str(e)}"
|
70 |
|
71 |
+
# --- Pydantic Models ---
|
72 |
class ContentPart(BaseModel): type: str; text: str
|
73 |
class ChatMessage(BaseModel): role: str; content: Union[str, List[ContentPart]]
|
74 |
class ChatCompletionRequest(BaseModel):
|
|
|
95 |
|
96 |
if not user_prompt: return {"error": "Prompt not found."}
|
97 |
|
|
|
|
|
98 |
initial_messages = [{'role': 'system', 'content': SYSTEM_PROMPT}, {'role': 'user', 'content': user_prompt}]
|
99 |
inputs = tokenizer.apply_chat_template(initial_messages, add_generation_prompt=True, return_tensors="pt").to(DEVICE)
|
100 |
+
|
101 |
+
# CORRECTION ICI : On utilise **inputs pour décompresser le dictionnaire
|
102 |
+
outputs = model.generate(**inputs, max_new_tokens=150, eos_token_id=tokenizer.eos_token_id)
|
103 |
+
thought_process = tokenizer.decode(outputs[0][len(inputs['input_ids'][0]):], skip_special_tokens=True)
|
104 |
|
105 |
tool_call = None
|
106 |
try:
|
|
|
107 |
json_part = thought_process[thought_process.find('{'):thought_process.rfind('}')+1]
|
108 |
if json_part:
|
109 |
tool_call = json.loads(json_part)
|
110 |
except json.JSONDecodeError:
|
111 |
tool_call = None
|
112 |
|
|
|
113 |
if tool_call and 'tool' in tool_call:
|
114 |
tool_context = ""
|
115 |
if tool_call['tool'] == 'browse' and 'url' in tool_call:
|
116 |
print(f"--- AGENT: Browsing URL: {tool_call['url']} ---")
|
117 |
tool_context = execute_browse_tool(tool_call['url'])
|
118 |
|
|
|
119 |
final_messages = [
|
120 |
{'role': 'system', 'content': SYSTEM_PROMPT},
|
121 |
{'role': 'user', 'content': user_prompt},
|
|
|
123 |
{'role': 'system', 'content': "Now, provide the final, complete answer to the user based on this information."}
|
124 |
]
|
125 |
else:
|
|
|
126 |
final_messages = [
|
127 |
{'role': 'system', 'content': SYSTEM_PROMPT},
|
128 |
{'role': 'user', 'content': user_prompt},
|
129 |
+
{'role': 'assistant', 'content': thought_process}
|
130 |
]
|
131 |
|
|
|
132 |
final_inputs = tokenizer.apply_chat_template(final_messages, add_generation_prompt=False, return_tensors="pt").to(DEVICE)
|
133 |
+
|
134 |
+
# DEUXIÈME CORRECTION ICI : On utilise également **final_inputs
|
135 |
+
final_outputs = model.generate(**final_inputs, max_new_tokens=1024, do_sample=True, temperature=0.1, top_k=50, top_p=0.95, eos_token_id=tokenizer.eos_token_id)
|
136 |
+
response_text = tokenizer.decode(final_outputs[0][len(final_inputs['input_ids'][0]):], skip_special_tokens=True)
|
137 |
|
138 |
async def stream_generator():
|
139 |
response_id = f"chatcmpl-{uuid.uuid4()}"
|
|
|
140 |
for char in response_text:
|
141 |
chunk = {"id": response_id, "object": "chat.completion.chunk", "created": int(time.time()), "model": MODEL_ID, "choices": [{"index": 0, "delta": {"content": char}, "finish_reason": None}]}
|
142 |
yield f"data: {json.dumps(chunk)}\n\n"
|