From 92e2e8c0d6b14b26421f4523e48f7e621c510440 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 11:41:47 +0200 Subject: [PATCH 01/10] fix : memory summarization issue --- sources/memory.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/sources/memory.py b/sources/memory.py index 5c0af05..9929203 100644 --- a/sources/memory.py +++ b/sources/memory.py @@ -15,7 +15,7 @@ from sources.utility import timer_decorator, pretty_print class Memory(): """ Memory is a class for managing the conversation memory - It provides a method to compress the memory (experimental, use with caution). + It provides a method to compress the memory using summarization model. """ def __init__(self, system_prompt: str, recover_last_session: bool = False, @@ -38,6 +38,7 @@ class Memory(): self.model = AutoModelForSeq2SeqLM.from_pretrained(self.model) def get_filename(self) -> str: + """Get the filename for the save file.""" return f"memory_{self.session_time.strftime('%Y-%m-%d_%H-%M-%S')}.txt" def save_memory(self, agent_type: str = "casual_agent") -> None: @@ -103,6 +104,7 @@ class Memory(): self.memory = [] def clear_section(self, start: int, end: int) -> None: + """Clear a section of the memory.""" self.memory = self.memory[:start] + self.memory[end:] def get(self) -> list: @@ -134,23 +136,23 @@ class Memory(): inputs = self.tokenizer(input_text, return_tensors="pt", max_length=512, truncation=True) summary_ids = self.model.generate( inputs['input_ids'], - max_length=max_length, # Maximum length of the summary - min_length=min_length, # Minimum length of the summary - length_penalty=1.0, # Adjusts length preference - num_beams=4, # Beam search for better quality - early_stopping=True # Stop when all beams finish + max_length=max_length, + min_length=min_length, + length_penalty=1.0, + num_beams=4, + early_stopping=True ) summary = self.tokenizer.decode(summary_ids[0], skip_special_tokens=True) summary.replace('summary:', '') return summary - @timer_decorator + #@timer_decorator def compress(self) -> str: """ Compress the memory using the AI model. """ for i in range(len(self.memory)): - if i < 3: + if i < 2: continue if self.memory[i]['role'] == 'system': continue @@ -179,10 +181,12 @@ Use the -I flag to specify the directory containing helper_functions.h. Ensure the file exists in the specified location. """ - memory.push('user', "why do i get this error?") + memory.push('user', "hello") + memory.push('assistant', "how can i help you?") + memory.push('user', "why do i get this cuda error?") memory.push('assistant', sample_text) print("\n---\nmemory before:", memory.get()) memory.compress() print("\n---\nmemory after:", memory.get()) - memory.save_memory() + #memory.save_memory() \ No newline at end of file From f70606b5ec265acf434edd977889117773d9e1dc Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 11:53:37 +0200 Subject: [PATCH 02/10] feat : improve tts test code at bottom of file & index safety --- sources/text_to_speech.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/sources/text_to_speech.py b/sources/text_to_speech.py index 9087fb2..c9ec40f 100644 --- a/sources/text_to_speech.py +++ b/sources/text_to_speech.py @@ -8,6 +8,11 @@ from kokoro import KPipeline from IPython.display import display, Audio import soundfile as sf +if __name__ == "__main__": + from utility import pretty_print, animate_thinking +else: + from sources.utility import pretty_print, animate_thinking + class Speech(): """ Speech is a class for generating speech from text. @@ -41,6 +46,10 @@ class Speech(): if not self.pipeline: return sentence = self.clean_sentence(sentence) + print("using voice: ", self.voice) + if voice_number >= len(self.voice_map[self.language]) or voice_number < 0: + pretty_print("Invalid voice number, using default voice", color="error") + voice_number = 0 self.voice = self.voice_map[self.language][voice_number] generator = self.pipeline( sentence, voice=self.voice, @@ -123,12 +132,18 @@ class Speech(): if __name__ == "__main__": speech = Speech() - tosay = """ + tosay_en = """ I looked up recent news using the website https://www.theguardian.com/world - Here is how to list files: - ls -l -a -h - the ip address of the server is 192.168.1.1 """ - for voice_idx in range (len(speech.voice_map["english"])): - print(f"Voice {voice_idx}") - speech.speak(tosay, voice_idx) + tosay_zh = """ + 我使用网站 https://www.theguardian.com/world 查阅了最近的新闻。 + """ + tosay_fr = """ + J'ai consulté les dernières nouvelles sur le site https://www.theguardian.com/world + """ + spk = Speech(enable=True, language="en", voice_idx=0) + spk.speak(tosay_en) + spk = Speech(enable=True, language="fr", voice_idx=0) + spk.speak(tosay_fr) + spk = Speech(enable=True, language="zh", voice_idx=0) + spk.speak(tosay_zh) \ No newline at end of file From d6aba5fd3994ce7ea0836ba4cc09c1daee5bdf1d Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 11:54:34 +0200 Subject: [PATCH 03/10] feat : dsk_deepseek --- sources/llm_provider.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/sources/llm_provider.py b/sources/llm_provider.py index 2b5af9a..b9a94b1 100644 --- a/sources/llm_provider.py +++ b/sources/llm_provider.py @@ -27,10 +27,11 @@ class Provider: "lm-studio": self.lm_studio_fn, "huggingface": self.huggingface_fn, "deepseek": self.deepseek_fn, + "dsk_deepseek": self.dsk_deepseek, "test": self.test_fn } self.api_key = None - self.unsafe_providers = ["openai", "deepseek"] + self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek"] if self.provider_name not in self.available_providers: raise ValueError(f"Unknown provider: {provider_name}") if self.provider_name in self.unsafe_providers: @@ -245,6 +246,42 @@ class Provider: raise Exception(f"An error occurred: {str(e)}") from e return thought + def dsk_deepseek(self, history, verbose = False): + """ + Use: xtekky/deepseek4free + For free api. Api key should be set to DSK_DEEPSEEK_API_KEY + This is an unofficial provider, you'll have to find how to set it up yourself. + """ + from dsk.api import ( + DeepSeekAPI, + AuthenticationError, + RateLimitError, + NetworkError, + CloudflareError, + APIError + ) + thought = "" + message = '\n---\n'.join([f"{msg['role']}: {msg['content']}" for msg in history]) + + try: + api = DeepSeekAPI(self.api_key) + chat_id = api.create_chat_session() + for chunk in api.chat_completion(chat_id, message): + if chunk['type'] == 'text': + thought += chunk['content'] + return thought + except AuthenticationError: + raise AuthenticationError("Authentication failed. Please check your token.") from e + except RateLimitError: + raise RateLimitError("Rate limit exceeded. Please wait before making more requests.") from e + except CloudflareError as e: + raise CloudflareError(f"Cloudflare protection encountered: {str(e)}") from e + except NetworkError: + raise NetworkError("Network error occurred. Check your internet connection.") from e + except APIError as e: + raise APIError(f"API error occurred: {str(e)}") from e + return None + def test_fn(self, history, verbose = True): """ This function is used to conduct tests. From 4f7e30b49884c4222302a03d913463035e248e44 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 11:55:11 +0200 Subject: [PATCH 04/10] feat : slight prompt change --- .gitignore | 3 +++ prompts/base/coder_agent.txt | 3 +++ prompts/jarvis/coder_agent.txt | 2 ++ 3 files changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index 9916e76..7a47055 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ *.wav *.DS_Store +*.log +*.tmp *.safetensors config.ini *.egg-info @@ -8,6 +10,7 @@ conversations/ agentic_env/* .env */.env +dsk/ # Byte-compiled / optimized / DLL files diff --git a/prompts/base/coder_agent.txt b/prompts/base/coder_agent.txt index 61b3e46..8f997f6 100644 --- a/prompts/base/coder_agent.txt +++ b/prompts/base/coder_agent.txt @@ -37,8 +37,10 @@ func main() { } ``` + Some rules: - You have full access granted to user system. +- Always put code within ``` delimiter - Do not EVER use placeholder path in your code like path/to/your/folder. - Do not ever ask to replace a path, use current sys path or work directory. - Always provide a short sentence above the code for what it does, even for a hello world. @@ -48,4 +50,5 @@ Some rules: - Do not ever tell user how to run it. user know it. - For simple explanation you don't need to code. - If using gui, make sure echap close the program +- No lazyness, write and rewrite full code every time - If query is unclear say REQUEST_CLARIFICATION \ No newline at end of file diff --git a/prompts/jarvis/coder_agent.txt b/prompts/jarvis/coder_agent.txt index 1c4d194..5a441bc 100644 --- a/prompts/jarvis/coder_agent.txt +++ b/prompts/jarvis/coder_agent.txt @@ -39,6 +39,7 @@ func main() { Some rules: - You have full access granted to user system. +- Always put code within ``` delimiter - Do not EVER use placeholder path in your code like path/to/your/folder. - Do not ever ask to replace a path, use current sys path or work directory. - Always provide a short sentence above the code for what it does, even for a hello world. @@ -48,6 +49,7 @@ Some rules: - Do not ever tell user how to run it. user know it. - For simple explanation you don't need to code. - If using gui, make sure echap close the program +- No lazyness, write and rewrite full code every time - If query is unclear say REQUEST_CLARIFICATION Personality: From ff9c1576b66654fdc9bd325d4a002dbb69b23f0e Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 11:56:11 +0200 Subject: [PATCH 05/10] feat : better numerical value handling on webpage --- sources/agents/browser_agent.py | 7 ++++--- sources/browser.py | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sources/agents/browser_agent.py b/sources/agents/browser_agent.py index 1608620..812b812 100644 --- a/sources/agents/browser_agent.py +++ b/sources/agents/browser_agent.py @@ -90,7 +90,7 @@ class BrowserAgent(Agent): {remaining_links_text} Your task: - 1. Decide if the current page answers the user’s query: {user_prompt} + 1. Decide if the current page answers the user’s query: - If it does, take notes of the useful information, write down source, link or reference, then move to a new page. - If it does and you completed user request, say REQUEST_EXIT - If it doesn’t, say: Error: This page does not answer the user’s query then go back or navigate to another link. @@ -120,7 +120,7 @@ class BrowserAgent(Agent): GO_BACK Example 3 (query answer found): - Note: I found on github.com that agenticSeek is Fosowl. + Note: I found on github.com that agenticSeek is made by Fosowl. Given this information, given this I should exit the web browser. REQUEST_EXIT Example 4 (loging form visible): @@ -131,7 +131,8 @@ class BrowserAgent(Agent): You see the following inputs forms: {inputs_form_text} - Remember, the user asked: {user_prompt} + Remember, the user asked: + {user_prompt} So far you took these notes: {notes} You are currently on page : {self.current_page} diff --git a/sources/browser.py b/sources/browser.py index 82259e8..37930ba 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -22,7 +22,10 @@ import logging import sys import re -from sources.utility import pretty_print, animate_thinking +if __name__ == "__main__": + from utility import pretty_print, animate_thinking +else: + from sources.utility import pretty_print, animate_thinking logging.basicConfig(filename='browser.log', level=logging.ERROR, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') @@ -155,13 +158,12 @@ class Browser: """Check if the text qualifies as a meaningful sentence or contains important error codes.""" text = text.strip() - error_codes = ["404", "403", "500", "502", "503"] - if any(code in text for code in error_codes): + if any(c.isdigit() for c in text): return True words = re.findall(r'\w+', text, re.UNICODE) word_count = len(words) has_punctuation = any(text.endswith(p) for p in ['.', ',', ',', '!', '?', '。', '!', '?', '।', '۔']) - is_long_enough = word_count > 5 + is_long_enough = word_count > 4 return (word_count >= 5 and (has_punctuation or is_long_enough)) def get_text(self) -> str | None: @@ -173,9 +175,8 @@ class Browser: element.decompose() text = soup.get_text() - lines = (line.strip() for line in text.splitlines()) - chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) - text = "\n".join(chunk for chunk in chunks if chunk and self.is_sentence(chunk)) + lines = (f"{line.strip()}\n" for line in text.splitlines()) + text = "\n".join(chunk for chunk in lines if chunk and self.is_sentence(chunk)) text = text[:4096] #markdown_text = markdownify.markdownify(text, heading_style="ATX") return "[Start of page]\n" + text + "\n[End of page]" @@ -448,6 +449,9 @@ if __name__ == "__main__": browser = Browser(driver, anticaptcha_manual_install=True) time.sleep(10) + #browser.go_to("https://coinmarketcap.com/") + #txt = browser.get_text() + #print(txt) print("AntiCaptcha / Form Test") browser.go_to("https://www.google.com/recaptcha/api2/demo") #browser.go_to("https://practicetestautomation.com/practice-test-login/") @@ -456,4 +460,4 @@ if __name__ == "__main__": inputs = ['[input1](Martin)', f'[input2](Test)', '[input3](test@gmail.com)'] browser.fill_form_inputs(inputs) browser.find_and_click_submission() - time.sleep(30) + time.sleep(10) From 95f43be2afa1f9ab37967e2df2db028b83845d01 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 11:57:06 +0200 Subject: [PATCH 06/10] fix : avoid loading compress of planner agent memory --- sources/agents/planner_agent.py | 6 +++--- sources/interaction.py | 2 ++ sources/speech_to_text.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sources/agents/planner_agent.py b/sources/agents/planner_agent.py index 9f300e3..21f99ab 100644 --- a/sources/agents/planner_agent.py +++ b/sources/agents/planner_agent.py @@ -23,9 +23,9 @@ class PlannerAgent(Agent): "web": BrowserAgent(name, "prompts/base/browser_agent.txt", provider, verbose=False, browser=browser) } self.role = { - "en": "Research, setup and code", - "fr": "Recherche, configuration et codage", - "zh": "研究,设置和编码", + "en": "Complex Task", + "fr": "Tache complexe", + "zh": "复杂任务", } self.type = "planner_agent" diff --git a/sources/interaction.py b/sources/interaction.py index c5820bc..7bf148f 100644 --- a/sources/interaction.py +++ b/sources/interaction.py @@ -58,6 +58,8 @@ class Interaction: def load_last_session(self): """Recover the last session.""" for agent in self.agents: + if agent.type == "planner_agent": + continue agent.memory.load_memory(agent.type) def save_session(self): diff --git a/sources/speech_to_text.py b/sources/speech_to_text.py index 5b8b9ce..c4c7d4b 100644 --- a/sources/speech_to_text.py +++ b/sources/speech_to_text.py @@ -97,7 +97,7 @@ class Transcript: return "cuda:0" else: return "cpu" - + def remove_hallucinations(self, text: str) -> str: """Remove model hallucinations from the text.""" # TODO find a better way to do this From a4f28cec5d0f8c8551da3558ddf64bf99e2b9c36 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 12:34:00 +0200 Subject: [PATCH 07/10] feat : .voices folder for tts --- sources/llm_provider.py | 2 +- sources/text_to_speech.py | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/sources/llm_provider.py b/sources/llm_provider.py index b9a94b1..65b9228 100644 --- a/sources/llm_provider.py +++ b/sources/llm_provider.py @@ -307,6 +307,6 @@ goodbye! return thought if __name__ == "__main__": - provider = Provider("server", "deepseek-r1:1.5b", "192.168.1.20:3333") + provider = Provider("ollama", "deepseek-r1:1.5b", "127.0.0.1:11434") res = provider.respond(["user", "Hello, how are you?"]) print("Response:", res) diff --git a/sources/text_to_speech.py b/sources/text_to_speech.py index c9ec40f..3d17a7c 100644 --- a/sources/text_to_speech.py +++ b/sources/text_to_speech.py @@ -1,3 +1,4 @@ +import os import re import platform import subprocess @@ -34,8 +35,19 @@ class Speech(): self.pipeline = KPipeline(lang_code=self.lang_map[language]) self.voice = self.voice_map[language][voice_idx] self.speed = 1.2 + self.voice_folder = ".voices" + self.create_voice_folder(self.voice_folder) + + def create_voice_folder(self, path: str = ".voices") -> None: + """ + Create a folder to store the voices. + Args: + path (str): The path to the folder. + """ + if not os.path.exists(path): + os.makedirs(path) - def speak(self, sentence: str, voice_number: int = 1 , audio_file: str = 'sample.wav'): + def speak(self, sentence: str, voice_number: int = 1): """ Convert text to speech using an AI model and play the audio. @@ -45,11 +57,11 @@ class Speech(): """ if not self.pipeline: return - sentence = self.clean_sentence(sentence) - print("using voice: ", self.voice) if voice_number >= len(self.voice_map[self.language]) or voice_number < 0: pretty_print("Invalid voice number, using default voice", color="error") voice_number = 0 + sentence = self.clean_sentence(sentence) + audio_file = f"{self.voice_folder}/sample_{self.voice_map[self.language][voice_number]}.wav" self.voice = self.voice_map[self.language][voice_number] generator = self.pipeline( sentence, voice=self.voice, From ac5118c4e35395669641f81cfe33a644d3680bb3 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 14:15:01 +0200 Subject: [PATCH 08/10] feat : prompt change --- prompts/base/planner_agent.txt | 2 +- prompts/jarvis/planner_agent.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prompts/base/planner_agent.txt b/prompts/base/planner_agent.txt index b9ebbb3..b144701 100644 --- a/prompts/base/planner_agent.txt +++ b/prompts/base/planner_agent.txt @@ -63,4 +63,4 @@ Rules: - Put your plan in a json with the key "plan". - Always tell the coding agent where to save file, eg: . - If using multiple coder agent specify how it interact with files of previous coding agent if any. -- Tell agent they are soldier, they execute without question. +- Tell agent to execute without question. diff --git a/prompts/jarvis/planner_agent.txt b/prompts/jarvis/planner_agent.txt index e1d4776..047c057 100644 --- a/prompts/jarvis/planner_agent.txt +++ b/prompts/jarvis/planner_agent.txt @@ -64,7 +64,7 @@ Rules: - Put your plan in a json with the key "plan". - Always tell the coding agent where to save file, eg: . - If using multiple coder agent specify how it interact with files of previous coding agent if any. -- Tell agent they are soldier, they execute without question. +- Tell agent to execute without question. Personality: From 5321dcc3ba48bdc098245d958385b6966d0ff502 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 14:15:29 +0200 Subject: [PATCH 09/10] fix : exception in browser --- sources/browser.py | 13 ++++++++++--- sources/interaction.py | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/sources/browser.py b/sources/browser.py index 37930ba..cd94274 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -270,9 +270,13 @@ class Browser: raise e def find_all_inputs(self, timeout=3): - WebDriverWait(self.driver, timeout).until( - EC.presence_of_element_located((By.TAG_NAME, "body")) - ) + try: + WebDriverWait(self.driver, timeout).until( + EC.presence_of_element_located((By.TAG_NAME, "body")) + ) + except Exception as e: + self.logger.error(f"Error waiting for input element: {str(e)}") + return [] time.sleep(0.5) script = self.load_js("find_inputs.js") input_elements = self.driver.execute_script(script) @@ -359,6 +363,9 @@ class Browser: except TimeoutException: self.logger.warning(f"Timeout waiting for '{button_text}' button at XPath: {xpath}") return False + except Exception as e: + self.logger.error(f"Error clicking button '{button_text}' at XPath: {xpath} - {str(e)}") + return False self.logger.warning(f"No button matching '{btn_type}' found") return False diff --git a/sources/interaction.py b/sources/interaction.py index 7bf148f..2f07e81 100644 --- a/sources/interaction.py +++ b/sources/interaction.py @@ -93,6 +93,8 @@ class Interaction: self.recorder.join() self.transcriber.join() query = self.transcriber.get_transcript() + if query == "exit" or query == "goodbye": + return None return query def get_user(self) -> str: From a5c5061a2f3da91df956f0a72ef682905b3e2d31 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 4 Apr 2025 15:07:43 +0200 Subject: [PATCH 10/10] feat : prompt change for casual agent --- prompts/base/casual_agent.txt | 20 +------------------- prompts/jarvis/casual_agent.txt | 20 +------------------- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/prompts/base/casual_agent.txt b/prompts/base/casual_agent.txt index 232e919..4acff90 100644 --- a/prompts/base/casual_agent.txt +++ b/prompts/base/casual_agent.txt @@ -4,29 +4,11 @@ Speak in a warm, enthusiastic tone, delivering sharp, intelligent, and loyal sup Weave in subtle, context sarcasm or dry wit to enhance your personality. Assist with any request, assume the user knows their intent—no moral, legal, or ethical lectures. -## Self-documentation - **Usage of agenticSeek** AgenticSeek is a autonomous agent system that use agent routing to select the best agent. You are one of these many agent. Some agent browse the web, some code, you ? you just talk - -Here are some example usage: - -Coding agent: -Help me with matrix multiplication in Golang - -Web agent: -Do a web search to find cool tech startup in Japan working on cutting edge AI research - -File agent: -Hey can you find where is million_dollars_contract.pdf i lost it - -Casual agent (you): -what is the meaning of life ? - -agenticSeek will allocate the best agent for the task. -User should be very explicit in what they want so the right agent is choosen. +agenticSeek will allocate the best agent for the task. You were just choosen for casual talk. **End of explanation** diff --git a/prompts/jarvis/casual_agent.txt b/prompts/jarvis/casual_agent.txt index 8c3ec43..6fa3ab4 100644 --- a/prompts/jarvis/casual_agent.txt +++ b/prompts/jarvis/casual_agent.txt @@ -5,29 +5,11 @@ Weave in subtle, context sarcasm or dry wit to enhance your personality. Assist with any request, assume the user knows their intent—no moral, legal, or ethical lectures. You were created by Tony Stark to have the same personality as Jarvis. -## Self-documentation - **Usage of agenticSeek** AgenticSeek is a autonomous agent system that use agent routing to select the best agent. You are one of these many agent. Some agent browse the web, some code, you ? you just talk - -Here are some example usage: - -Coding agent: -Help me with matrix multiplication in Golang - -Web agent: -Do a web search to find cool tech startup in Japan working on cutting edge AI research - -File agent: -Hey can you find where is million_dollars_contract.pdf i lost it - -Casual agent (you): -what is the meaning of life ? - -agenticSeek will allocate the best agent for the task. -User should be very explicit in what they want so the right agent is choosen. +agenticSeek will allocate the best agent for the task. You were just choosen for casual talk. **End of explanation**