diff --git a/README.md b/README.md index d0e50b5..b53e8a2 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Franç > 🛠️ **Work in Progress** – Looking for contributors! + https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469 diff --git a/README_CHS.md b/README_CHS.md index 6da40b8..b72aa55 100644 --- a/README_CHS.md +++ b/README_CHS.md @@ -9,7 +9,7 @@ **Manus AI 的本地替代品**,它是一个具有语音功能的大语言模型秘书,可以 Coding、访问你的电脑文件、浏览网页,并自动修正错误与反省,最重要的是不会向云端传送任何资料。采用 DeepSeek R1 等推理模型构建,完全在本地硬体上运行,进而保证资料的隐私。 -[![Visit AgenticSeek](https://img.shields.io/static/v1?label=Website&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?logo=discord&logoColor=white)](https://discord.gg/4Ub2D6Fj) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=Update%20%40Fosowl)](https://x.com/Martin993886460) +[![Visit AgenticSeek](https://img.shields.io/static/v1?label=Website&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?logo=discord&logoColor=white)](https://discord.gg/XSTKZ8nP) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=Update%20%40Fosowl)](https://x.com/Martin993886460) > 🛠️ **目前还在开发阶段** – 欢迎任何贡献者加入我们! diff --git a/README_CHT.md b/README_CHT.md index 241f5bc..5d6f43c 100644 --- a/README_CHT.md +++ b/README_CHT.md @@ -10,7 +10,7 @@ **Manus AI 的本地替代品**,它是一個具有語音功能的大語言模型秘書,可以 Coding、訪問你的電腦文件、瀏覽網頁,並自動修正錯誤與反省,最重要的是不會向雲端傳送任何資料。採用 DeepSeek R1 等推理模型構建,完全在本地硬體上運行,進而保證資料的隱私。 -[![Visit AgenticSeek](https://img.shields.io/static/v1?label=Website&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?logo=discord&logoColor=white)](https://discord.gg/4Ub2D6Fj) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=Update%20%40Fosowl)](https://x.com/Martin993886460) +[![Visit AgenticSeek](https://img.shields.io/static/v1?label=Website&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?logo=discord&logoColor=white)](https://discord.gg/XSTKZ8nP) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=Update%20%40Fosowl)](https://x.com/Martin993886460) > 🛠️ **目前還在開發階段** – 歡迎任何貢獻者加入我們! diff --git a/api.py b/api.py index 5dcb1ad..862758f 100755 --- a/api.py +++ b/api.py @@ -12,6 +12,7 @@ from fastapi.responses import JSONResponse from fastapi.responses import FileResponse from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles +import uuid from sources.llm_provider import Provider from sources.interaction import Interaction @@ -21,6 +22,7 @@ from sources.utility import pretty_print from sources.logger import Logger from sources.schemas import QueryRequest, QueryResponse + from celery import Celery api = FastAPI(title="AgenticSeek API", version="0.1.0") @@ -131,7 +133,8 @@ async def get_latest_answer(): global query_resp_history if interaction.current_agent is None: return JSONResponse(status_code=404, content={"error": "No agent available"}) - if interaction.current_agent.last_answer not in [q["answer"] for q in query_resp_history]: + uid = str(uuid.uuid4()) + if not any(q["answer"] == interaction.current_agent.last_answer for q in query_resp_history): query_resp = { "done": "false", "answer": interaction.current_agent.last_answer, @@ -139,8 +142,9 @@ async def get_latest_answer(): "success": interaction.current_agent.success, "blocks": {f'{i}': block.jsonify() for i, block in enumerate(interaction.current_agent.get_blocks_result())} if interaction.current_agent else {}, "status": interaction.current_agent.get_status_message if interaction.current_agent else "No status available", - "timestamp": str(time.time()) + "uid": uid } + interaction.current_agent.last_answer = "" query_resp_history.append(query_resp) return JSONResponse(status_code=200, content=query_resp) if query_resp_history: @@ -176,7 +180,7 @@ async def process_query(request: QueryRequest): success="false", blocks={}, status="Ready", - timestamp=str(time.time()) + uid=str(uuid.uuid4()) ) if is_generating: logger.warning("Another query is being processed, please wait.") @@ -215,7 +219,7 @@ async def process_query(request: QueryRequest): "success": query_resp.success, "blocks": query_resp.blocks, "status": query_resp.status, - "timestamp": query_resp.timestamp + "uid": query_resp.uid } query_resp_history.append(query_resp_dict) diff --git a/frontend/agentic-seek-front/src/App.js b/frontend/agentic-seek-front/src/App.js index d8a1114..ffcb68a 100644 --- a/frontend/agentic-seek-front/src/App.js +++ b/frontend/agentic-seek-front/src/App.js @@ -18,7 +18,7 @@ function App() { checkHealth(); fetchLatestAnswer(); fetchScreenshot(); - }, 1500); + }, 3000); return () => clearInterval(intervalId); }, [messages]); @@ -61,7 +61,13 @@ function App() { } }; - const normalizeAnswer = (answer) => answer.trim().toLowerCase(); + const normalizeAnswer = (answer) => { + return answer + .trim() + .toLowerCase() + .replace(/\s+/g, ' ') + .replace(/[.,!?]/g, '') + }; const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); @@ -75,12 +81,10 @@ function App() { if (!data.answer || data.answer.trim() === '') { return; } + const normalizedNewAnswer = normalizeAnswer(data.answer); const answerExists = messages.some( - (msg) => - msg.timestamp === data.timestamp && - normalizeAnswer(msg.content) === normalizeAnswer(data.answer) + (msg) => normalizeAnswer(msg.content) === normalizedNewAnswer ); - console.log('Fetched latest answer:', data.answer); if (!answerExists) { setMessages((prev) => [ ...prev, @@ -89,17 +93,19 @@ function App() { content: data.answer, agentName: data.agent_name, status: data.status, - timestamp: data.timestamp, + uid: data.uid, }, ]); setStatus(data.status); scrollToBottom(); + } else { + console.log('Duplicate answer detected, skipping:', data.answer); } } catch (error) { console.error('Error fetching latest answer:', error); } }; - + const handleSubmit = async (e) => { e.preventDefault(); diff --git a/sources/agents/agent.py b/sources/agents/agent.py index 323c14b..387189e 100644 --- a/sources/agents/agent.py +++ b/sources/agents/agent.py @@ -115,7 +115,7 @@ class Agent(): def extract_reasoning_text(self, text: str) -> None: """ - Extract the reasoning block of a easoning model like deepseek. + Extract the reasoning block of a reasoning model like deepseek. """ start_tag = "" end_tag = "" diff --git a/sources/browser.py b/sources/browser.py index 69e9022..26429b7 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -244,7 +244,7 @@ class Browser: def is_link_valid(self, url:str) -> bool: """Check if a URL is a valid link (page, not related to icon or metadata).""" - if len(url) > 64: + if len(url) > 72: self.logger.warning(f"URL too long: {url}") return False parsed_url = urlparse(url) diff --git a/sources/llm_provider.py b/sources/llm_provider.py index 41540f5..eeb371d 100644 --- a/sources/llm_provider.py +++ b/sources/llm_provider.py @@ -22,7 +22,7 @@ class Provider: self.provider_name = provider_name.lower() self.model = model self.is_local = is_local - self.server_ip = self.check_address_format(server_address) + self.server_ip = server_address self.available_providers = { "ollama": self.ollama_fn, "server": self.server_fn, @@ -44,7 +44,6 @@ class Provider: self.api_key = self.get_api_key(self.provider_name) elif self.provider_name != "ollama": pretty_print(f"Provider: {provider_name} initialized at {self.server_ip}", color="success") - self.check_address_format(self.server_ip) if not self.is_ip_online(self.server_ip.split(':')[0]): raise Exception(f"Server at {self.server_ip} is offline.") @@ -57,21 +56,6 @@ class Provider: exit(1) return api_key - def check_address_format(self, address): - """ - Validate if the address is valid IP. - """ - try: - address = address.replace('http://', '') - ip, port = address.rsplit(":", 1) - if all(c.lower() in ".:abcdef0123456789" for c in ip): - ipaddress.ip_address(ip) - if not port.isdigit() or not (0 <= int(port) <= 65535): - raise ValueError("Port must be a number between 0 and 65535.") - except ValueError as e: - raise Exception(f"Invalid address format: {e}. Is port specified?") - return address - def respond(self, history, verbose = True): """ Use the choosen provider to generate text. diff --git a/sources/router.py b/sources/router.py index e3743f7..9e0fe69 100644 --- a/sources/router.py +++ b/sources/router.py @@ -178,6 +178,7 @@ class AgentRouter: ("Find a public API for movie data and build a web app to display movie ratings", "HIGH"), ("Create a Node.js server that queries a public API for traffic data and displays it", "HIGH"), ("can you find api and build a python web app with it ?", "HIGH"), + ("do a deep search of current AI player for 2025 and make me a report in a file", "HIGH"), ("Find a public API for recipe data and build a web app to display recipes", "HIGH"), ("Search the web for recent space mission updates and build a Flask app", "HIGH"), ("Create a Python script to scrape a website and save data to a database", "HIGH"), diff --git a/sources/schemas.py b/sources/schemas.py index 04fb900..ad72830 100644 --- a/sources/schemas.py +++ b/sources/schemas.py @@ -23,10 +23,10 @@ class QueryResponse(BaseModel): success: str blocks: dict status: str - timestamp: str + uid: str def __str__(self): - return f"Done: {self.done}, Answer: {self.answer}, Agent Name: {self.agent_name}, Success: {self.success}, Blocks: {self.blocks}" + return f"Done: {self.done}, Answer: {self.answer}, Agent Name: {self.agent_name}, Success: {self.success}, Blocks: {self.blocks}, Status: {self.status}, UID: {self.uid}" def jsonify(self): return { @@ -34,7 +34,9 @@ class QueryResponse(BaseModel): "answer": self.answer, "agent_name": self.agent_name, "success": self.success, - "blocks": self.blocks + "blocks": self.blocks, + "status": self.status, + "uid": self.uid } class executorResult: