diff --git a/__init__.py b/__init__.py index 7c613da..4f09cac 100644 --- a/__init__.py +++ b/__init__.py @@ -4,4 +4,4 @@ JIVAS is an Agentic Framework for rapidly prototyping and deploying graph-based, AI solutions. """ -__version__ = "2.1.23" +__version__ = "2.1.24" diff --git a/core/jivas/agent/action/action.jac b/core/jivas/agent/action/action.jac index 8c9445c..c1c1c7a 100644 --- a/core/jivas/agent/action/action.jac +++ b/core/jivas/agent/action/action.jac @@ -30,7 +30,11 @@ node Action(GraphNode) { # override to execute operations upon registration of action def on_register() { } - def run(frame_node:Frame, interaction_node:Interaction){} + # overide to execute operations upon running of action + def run(frame_node:Frame, interaction_node:Interaction){ } + + # overide to execute operations upon denying access of action + def deny(interaction_node:Interaction){ } # override to execute operations upon the reload of action def on_reload() { } diff --git a/core/jivas/agent/action/retrieval_action.jac b/core/jivas/agent/action/retrieval_action.jac new file mode 100644 index 0000000..d7c6b02 --- /dev/null +++ b/core/jivas/agent/action/retrieval_action.jac @@ -0,0 +1,221 @@ +import json; +import logging; +import traceback; +import from typing { Optional, Union } +import from logging { Logger } +import from jivas.agent.action.action { Action } +import from jivas.agent.action.model_action { ModelAction, ModelActionResult } + +node RetrievalAction(Action) { + # Integrates with vector database for retrieval augmented generation tasks + + # set up logger + static has logger:Logger = logging.getLogger(__name__); + + # the directive template for RAG + has directive:str = """ + Use CONTEXT as your knowledge base, intelligently assess the user question, review CONTEXT for context and finally produce an informative and accurate response. + Do not include any information outside of the CONTEXT. If relevant content is not available in CONTEXT, advise the user that you do not have the relevant information at this time. + + CONTEXT: + {context} + + """; + + # the null directive template for RAG + has null_directive:str = "No context information was retrieved based on user utterance. If there is no other information available in this prompt or the converation history that can help answer the user's question, advise the user that you do not have the relevant information at this time to answer their question.\n"; + + # context_rewriting_prompt + has query_completion_prompt:str = """ + Based on the conversation history, perform the following tasks: + + 1. **Analyze Context and Intent**: + - Review the conversation history to establish context + - Determine if the user's message is a query requiring information + - Skip refinement for small talk, greetings, or acknowledgments + + 2. **Query Refinement** (if applicable): + - Enhance the query by incorporating key context from conversation history + - Make implicit references explicit using historical context + - Ensure the query is specific, clear, and self-contained + - Remove ambiguous pronouns or references + - Maintain original intent while improving clarity + + 3. **Output Format**: + Return a JSON object (no delimiters!) with the following keys: + - "query": "the refined query or original message", + - "is_query": true/false (true if message requires context search, false otherwise) + + Note: Focus solely on query clarification and refinement. + The 'query' field should contain only the refined query or original message without commentary. + The 'is_query' field should be true for information-seeking questions and false for casual conversation. + + """; + + # the number of results + has k:int = 3; + # the score threshold (smaller numbers are usually more accurate) + has score_threshold:float = 0.3; + # max marginal relevance search + has mmr:bool = False; + # whether to return metadata with context or not + has metadata:bool = False; + # the vector store action name bound to this retrieval action + has vector_store_action:str = ""; + has history_size:int = 3; + has max_statement_length:int = 400; + has model_action:str = "LangChainModelAction"; + has model_name:str = "gpt-4o"; + has model_temperature:float = 0.2; + has model_max_tokens:int = 10000; + + def on_register() { + + # load the agent's default vector store action if none is specified + if not self.vector_store_action { + self.vector_store_action = (self.get_agent()).vector_store_action; + } + } + + def run(frame_node: Frame, interaction_node: Interaction) { + + # first prepare the query with context completion + # prepare query using conversation history or fallback to original utterance + query = self.process_query(frame_node, interaction_node); + + if(not query) { + # if no query is generated, return early + return; + } + + if not query.get("is_query", False) { + # if the query is not a query, return early + return; + } + + # update interaction node with query and context data + interaction_context = interaction_node.data_get(key=self.get_type()); + if not interaction_context { + interaction_context = {}; + } + interaction_context['query'] = query.get("query", interaction_node.utterance); + + # handle context, if any and queue directive + if(context_data := self.retrieve_context(interaction_context['query'])) { + + context_directive = None; + # add raw context to the interaction node + interaction_context['context'] = context_data; + # convert context data to JSON for composing the directive + context_json = json.dumps(context_data); + # prepare context directive + context_directive = self.directive.format(context=context_json); + # add the context directive to the interaction node + interaction_node.add_directive(directive = context_directive); + } else { + interaction_node.add_directive(directive = self.null_directive); + } + + interaction_node.data_set(key=self.get_type(), value=interaction_context); + + } + + def process_query(frame_node: Frame, interaction_node: Interaction) -> dict { + + query = {}; + + # grab the history, if any + if (statements := frame_node.get_transcript_statements(interactions = self.history_size, max_statement_length = self.max_statement_length)) { + + prompt_messages = []; + prompt_messages.extend(statements); + prompt_messages.extend([{"human": interaction_node.utterance}]); + prompt_messages.extend([{"system": self.query_completion_prompt}]); + + result = None; + + if(model_action := self.get_agent().get_action(action_label=self.model_action)) { + + if( model_action_result := model_action.call_model( + prompt_messages = prompt_messages, + prompt_variables = {}, + interaction_node = interaction_node, + model_name=self.model_name, + model_temperature=self.model_temperature, + model_max_tokens=self.model_max_tokens + )) { + # add the resulting intent, if any to the interaction to trigger the relevant action(s) + query = model_action_result.get_json_result(); + } + } + } + + return query; + } + + + def retrieve_context(query:str, filter:Optional[str] = "") -> list { + # override to implement custom retrieval operation + + # """ + # retrieves document for context + + # :param interaction_node (Interaction) – interaction node containing utterance, etc. + + # :returns context data relevant for RAG or [] if no context is found + # """ + context_data = []; + + if(vector_store_action := self.get_agent().get_action(action_label=self.vector_store_action)) { + + if(self.mmr) { + if(documents := vector_store_action.max_marginal_relevance_search(query=query, k=self.k)) { + for doc in documents { + context_item = { + "content": doc.page_content + }; + if(self.metadata) { + context_item["metadata"] = doc.metadata; + } + context_data.append(context_item); + } + if context_data { + return json.dumps(context_data); + } + } + } else { + # perform similarity search + if(documents_and_score := vector_store_action.similarity_search_with_score(query=query, k=self.k, filter=filter)) { + for (doc, score) in documents_and_score { + if(score <= self.score_threshold) { + context_item = { + "content": doc.page_content + }; + if(self.metadata) { + context_item["metadata"] = doc.metadata; + } + context_data.append(context_item); + } + } + } + } + } + + return context_data; + } + + def healthcheck() -> Union[bool, dict] { + + vector_store_action = self.get_agent().get_action(action_label=self.vector_store_action); + if(not vector_store_action) { + return { + "status": False, + "message": f"Unable to find a valid vector store action. Check your configuration and try again.", + "severity": "error" + }; + } + + return True; + } + +} \ No newline at end of file diff --git a/core/jivas/agent/action/subgraph_action/completed_state.jac b/core/jivas/agent/action/subgraph_action/completed_state.jac index 7d0ddbc..7e91ae6 100644 --- a/core/jivas/agent/action/subgraph_action/completed_state.jac +++ b/core/jivas/agent/action/subgraph_action/completed_state.jac @@ -5,7 +5,7 @@ import from logging { Logger } import from jivas.agent.core.graph_node { GraphNode } import from jivas.agent.action.subgraph_action.state { State } import from jivas.agent.memory.frame { Frame } - +import from jivas.agent.memory.interaction { Interaction } node CompletedState(GraphNode) { @@ -13,7 +13,7 @@ node CompletedState(GraphNode) { has collection_id: str = ""; has auto_confirm: bool = False; has model_action: str = "LangChainModelAction"; - has model_name: str = "gpt-4o"; + has model_name: str = "gpt-4.1"; has model_max_tokens:int = 4096; has model_temperature: float = 0.3; has history: bool = True; @@ -29,7 +29,7 @@ node CompletedState(GraphNode) { - Extract all user-provided submission details from: {summary} - Format them as a clear, bulleted list under a statement that indicates that it is a list of the information gathered. - - Do not put internal settings such as "revised" or "confirm response" in the displayed list + - Do not put internal settings such as "revised", "completed", "confirm_response" in the displayed list - Do not put any items on the list with a value of N/A b. Request Explicit Confirmation: Present the key and summary followed by: @@ -42,7 +42,7 @@ node CompletedState(GraphNode) { Follow these rules: # Confirmation Detection - Set "confirm_response" to true for: "yes", "sure", "confirmed", "yeah", "yep", "absolutely", "okay" + clear context. + Set "confirm_response" to true for: "yes", "sure", "confirmed", "yeah", "yep", "absolutely", "okay" + clear context. These should be in response to a request for confirmation of previously provided information. Set "confirm_response" to false ONLY for explicit negative or revision signals, including: - Direct negatives: "no" - Revision or correction requests: "I'd like to make an adjustment", "need to make a change", "revision", "change my answer", "not correct", "incorrect", "needs update" @@ -64,9 +64,10 @@ node CompletedState(GraphNode) { return True; } - def run(frame:Frame) -> Union[str, bool] { + def run(frame:Frame, interaction_node:Interaction) -> Union[str, bool] { frame_node = frame.frame_node; agent_node = frame.agent_node; + self.update_responses({"completed": True}, frame); states_data = frame.frame_node.data_get(key=f"{frame.action_label}_results"); revised = states_data.get("revised", False); @@ -77,7 +78,7 @@ node CompletedState(GraphNode) { } elif not revised{ # Check for confirmation in the latest user message - branch_choice_response = self.call_llm(self.prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_node); + branch_choice_response = self.call_llm(self.prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_node, interaction_node=interaction_node); self.update_responses(branch_choice_response, frame); } elif revised{ @@ -117,7 +118,7 @@ node CompletedState(GraphNode) { } } - def call_llm(prompt:str, history:Union[bool,None] = None, json_only:bool = False, frame_node:Frame, agent_node:GraphNode) -> Union[str, dict, None] { + def call_llm(prompt:str, history:Union[bool,None] = None, json_only:bool = False, frame_node:Frame, agent_node:GraphNode, interaction_node:Interaction) -> Union[str, dict, None] { # performs function tool calling for extracting question responses based on question prompt_messages = []; @@ -159,7 +160,8 @@ node CompletedState(GraphNode) { prompt_variables={}, model_name=self.model_name, model_temperature=self.model_temperature, - model_max_tokens=self.model_max_tokens + model_max_tokens=self.model_max_tokens, + interaction_node=interaction_node ); if model_action_result { @@ -176,7 +178,6 @@ node CompletedState(GraphNode) { } def update_responses(responses:dict, frame:Frame) { - print(f"New responses to merge: {responses}"); stored_responses = frame.frame_node.data_get(key=f"{frame.action_label}_results"); if type(stored_responses) is not dict or not stored_responses { diff --git a/core/jivas/agent/action/subgraph_action/confirmed_state.jac b/core/jivas/agent/action/subgraph_action/confirmed_state.jac index 2dd4a6f..3dda082 100644 --- a/core/jivas/agent/action/subgraph_action/confirmed_state.jac +++ b/core/jivas/agent/action/subgraph_action/confirmed_state.jac @@ -4,7 +4,7 @@ import from typing { Union } import from logging { Logger } import from jivas.agent.action.subgraph_action.state { State } import from jivas.agent.memory.frame { Frame } - +import from jivas.agent.memory.interaction { Interaction } node ConfirmedState(State) { # Represents an execution on a subgraph on the agent action graph @@ -25,7 +25,7 @@ node ConfirmedState(State) { return True; } - def run(frame:Frame) { + def run(frame:Frame, interaction_node:Interaction) -> Union[str, bool] { frame_node = frame.frame_node; agent_node = frame.agent_node; diff --git a/core/jivas/agent/action/subgraph_action/initial_state.jac b/core/jivas/agent/action/subgraph_action/initial_state.jac index ae4c95f..dfdd8ab 100644 --- a/core/jivas/agent/action/subgraph_action/initial_state.jac +++ b/core/jivas/agent/action/subgraph_action/initial_state.jac @@ -5,7 +5,7 @@ import from logging { Logger } import from jivas.agent.action.subgraph_action.state { State } import from jivas.agent.core.graph_node { GraphNode } import from jivas.agent.memory.frame { Frame } - +import from jivas.agent.memory.interaction { Interaction } node InitialState(State) { # Represents an execution on a subgraph on the agent action graph @@ -39,19 +39,20 @@ node InitialState(State) { return True; } - def run(frame:Frame) { + def run(frame:Frame, interaction_node:Interaction) -> Union[str, bool] { frame_node = frame.frame_node; agent_node = frame.agent_node; - + interaction_node = frame.interaction_node; # check if abort was set to true if self.abort{ frame_node.data_set(key=f"{frame.action_label}_results", value={}); self.abort = False; return ""; }else{ - response = self.call_llm(self.prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_node); + response = self.call_llm(self.prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_node, interaction_node=interaction_node); if response.get("abort_response"){ frame_node.data_set(key=f"{frame.action_label}_results", value={}); + interaction_node.add_event("AI has aborted the current process by request of the user."); return self.directive; } else{ diff --git a/core/jivas/agent/action/subgraph_action/revision_state.jac b/core/jivas/agent/action/subgraph_action/revision_state.jac index dc011ba..8cc94cc 100644 --- a/core/jivas/agent/action/subgraph_action/revision_state.jac +++ b/core/jivas/agent/action/subgraph_action/revision_state.jac @@ -5,6 +5,7 @@ import from logging { Logger } import from jivas.agent.core.graph_node { GraphNode } import from jivas.agent.action.subgraph_action.state { State } import from jivas.agent.memory.frame { Frame } +import from jivas.agent.memory.interaction { Interaction } @@ -15,7 +16,7 @@ node RevisionState(GraphNode) { has state_info:dict = {}; has states:list = []; has model_action: str = "LangChainModelAction"; - has model_name: str = "gpt-4o"; + has model_name: str = "gpt-4.1"; has model_max_tokens:int = 4096; has model_temperature: float = 0.3; has history: bool = True; @@ -59,13 +60,13 @@ node RevisionState(GraphNode) { } } - def run(frame:Frame) -> Union[str, bool] { + def run(frame:Frame, interaction_node:Interaction) -> Union[str, bool] { frame_node = frame.frame_node; agent_node = frame.agent_node; # Check for any changes in the latest user message prompt = self.generate_revision_extraction_prompt(frame); - extraction_result = self.call_llm(prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_node); + extraction_result = self.call_llm(prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_node, interaction_node=interaction_node); # If there is no confirmation or abortion if extraction_result{ @@ -124,7 +125,7 @@ node RevisionState(GraphNode) { return prompt; } - def call_llm(prompt:str, history:Union[bool,None] = None, json_only:bool = False, frame_node:Frame, agent_node:GraphNode) -> Union[str, dict, None] { + def call_llm(prompt:str, history:Union[bool,None] = None, json_only:bool = False, frame_node:Frame, agent_node:GraphNode, interaction_node: Interaction) -> Union[str, dict, None] { # performs function tool calling for extracting question responses based on question prompt_messages = []; @@ -165,7 +166,8 @@ node RevisionState(GraphNode) { prompt_variables={}, model_name=self.model_name, model_temperature=self.model_temperature, - model_max_tokens=self.model_max_tokens + model_max_tokens=self.model_max_tokens, + interaction_node=interaction_node ); if model_action_result { diff --git a/core/jivas/agent/action/subgraph_action/state.jac b/core/jivas/agent/action/subgraph_action/state.jac index e5b39d5..e6702a7 100644 --- a/core/jivas/agent/action/subgraph_action/state.jac +++ b/core/jivas/agent/action/subgraph_action/state.jac @@ -1,9 +1,10 @@ import logging; import traceback; -import from typing { Union } import from logging { Logger } import from jivas.agent.core.graph_node { GraphNode } import from jivas.agent.memory.frame { Frame } +import from typing { Union } +import from jivas.agent.memory.interaction { Interaction } import from jivas.agent.action.action { Action } node State(GraphNode) { @@ -18,12 +19,22 @@ node State(GraphNode) { #llm model parameters has model_action: str = "LangChainModelAction"; - has model_name: str = "gpt-4o"; + has model_name: str = "gpt-4.1"; has model_max_tokens:int = 4096; has model_temperature: float = 0.3; has history: bool = True; has history_size: int = 3; has max_statement_length: int = 2048; + has directive_template:str = """ + Tailor your response to get the information needed based on the following description: + {description} + + Avoid asking for other information not related to this description unless specified elsewhere. + {question} + + Take note of the following additional instructions if present while responding to the user but do not mention them unless it is needed: + {instructions} + """; has extraction_prompt:str = """ Review the user's message and the conversation history to accurately extract the following entities. @@ -89,21 +100,31 @@ node State(GraphNode) { } - def run(frame:Frame)->Union[str, bool]{ + def run(frame:Frame, interaction_node:Interaction)->Union[str, bool]{ agent_node = frame.agent_node; frame_node = frame.frame_node; required = self.state_info.get("required",""); prompt = self.generate_extraction_prompt(); - extraction_result = self.call_llm(prompt=prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_node); - + extraction_result = self.call_llm( + prompt=prompt, + history=True, + json_only=True, + frame_node=frame_node, + interaction_node=interaction_node, + agent_node=agent_node + ); if required is True and not extraction_result{ + question = self.state_info.get("question", ""); constraints = self.state_info.get("constraints", {}); description = constraints.get("description", ""); + additional_instructions = constraints.get("additional_instructions", ""); - directive = "Tailor your response to get the information needed based on the following description: \n" + description + "\n Eg." + question; + directive = self.directive_template.replace("{description}", description); + directive = directive.replace("{instructions}", additional_instructions); + directive = directive.replace("{question}", f"E.g. {question}"); if(options:= constraints.get("options", "")){ directive = directive + "\n They can choose from the list of options below\n" + str(options); @@ -142,7 +163,7 @@ node State(GraphNode) { entities = "\n".join(entities_list); sample_json = '{\n' + ',\n'.join(sample_json_lines) + '\n}'; - # prepate the prompt + # prepare the prompt prompt = self.extraction_prompt.format(entities=entities, sample_json=sample_json); # escape the conflicting symbols prompt = prompt.replace('{', '{{').replace('}','}}'); @@ -154,7 +175,7 @@ node State(GraphNode) { } } - def call_llm(prompt:str, history:Union[bool,None] = None, json_only:bool = False, frame_node:Frame, agent_node:GraphNode) -> Union[str, dict, None] { + def call_llm(prompt:str, history:Union[bool,None] = None, json_only:bool = False, frame_node:Frame, interaction_node:Interaction, agent_node:GraphNode) -> Union[str, dict, None] { # performs function tool calling for extracting question responses based on question prompt_messages = []; @@ -195,7 +216,8 @@ node State(GraphNode) { prompt_variables={}, model_name=self.model_name, model_temperature=self.model_temperature, - model_max_tokens=self.model_max_tokens + model_max_tokens=self.model_max_tokens, + interaction_node=interaction_node ); if model_action_result { diff --git a/core/jivas/agent/action/subgraph_action/subgraph_action.jac b/core/jivas/agent/action/subgraph_action/subgraph_action.jac index ff7fec4..6ff365e 100644 --- a/core/jivas/agent/action/subgraph_action/subgraph_action.jac +++ b/core/jivas/agent/action/subgraph_action/subgraph_action.jac @@ -13,7 +13,6 @@ import from jivas.agent.action.subgraph_action.confirmed_state { ConfirmedState import from jivas.agent.action.subgraph_action.revision_state { RevisionState } import from jivas.agent.action.subgraph_action.initial_state { InitialState } import from jivas.agent.memory.collection { Collection } -import from jivas.agent.memory.frame { Frame } import from jivas.agent.core.graph_walker { graph_walker } import from jivas.agent.core.graph_node { GraphNode } @@ -31,6 +30,14 @@ node SubgraphAction(Action) { has revision_state: dict = {}; has parameters: list = []; + #llm model parameters + has model_action: str = "LangChainModelAction"; + has model_name: str = "gpt-4.1"; + has model_max_tokens:int = 4096; + has max_statement_length:int = 500; + has model_temperature: float = 0.3; + has history: bool = True; + has history_size: int = 3; # set up logger static has logger:Logger = logging.getLogger(__name__); @@ -196,7 +203,7 @@ node SubgraphAction(Action) { elif type(pre_process) is list{ directives = pre_process; } - walker_result = self.walk_subgraph(frame_node); + walker_result = self.walk_subgraph(frame_node, interaction_node); directives.extend(walker_result.directives); walker_result.directives = {}; @@ -207,6 +214,7 @@ node SubgraphAction(Action) { if custom_directives{ directives = (custom_directives); } + self.update_response(responses, frame_node); if (responses.get("confirm_response", "")){ frame_node.data_set(key=f"{self.label}_results", value={}); @@ -218,12 +226,66 @@ node SubgraphAction(Action) { self.directives.append(directive); } - def walk_subgraph(frame_node:Frame) -> Union[str, dict] { + def walk_subgraph(frame_node:Frame, interaction_node:Interaction) -> Union[str, dict] { collection = self.get_collection(); - walker_result = collection spawn _subgraph(frame_node=frame_node, agent_node=&self.agent_id, action_label=self.label); + walker_result = collection spawn _subgraph(frame_node=frame_node, interaction_node=interaction_node, agent_node=&self.agent_id, action_label=self.label); return walker_result; } + def call_llm(prompt:str, frame_node:Frame, interaction_node:Interaction, history:Union[bool,None] = None, json_only:bool = False) -> Union[str, dict, None] { + # performs function tool calling for extracting question responses based on question + prompt_messages = []; + + if not prompt { + return None; + } + + use_history = self.history; + if history is not None { + use_history = history; + } + + prompt_messages = [ + {"human":interaction_node.utterance}, + {"system":prompt} + ]; + + # prepare the final prompt with history. + if (use_history) { + statements = frame_node.get_transcript_statements(interactions = self.history_size, max_statement_length = self.max_statement_length, with_events = True); + + if (statements) { + # prepend statements to the prompt messages + prompt_messages = statements + prompt_messages; + } + + } + + model_action = self.get_agent().get_action(action_label=self.model_action); + + if model_action { + model_action_result = model_action.call_model( + prompt_messages=prompt_messages, + prompt_variables={}, + model_name=self.model_name, + model_temperature=self.model_temperature, + model_max_tokens=self.model_max_tokens, + interaction_node=interaction_node + ); + + if model_action_result { + if json_only { + return model_action_result.get_json_result(); + } else { + return model_action_result.get_result(); + } + } + else { + return None; + } + } + } + def update_response(responses:dict, frame:Frame) { stored_responses = frame.data_get(key=f"{self.label}_results"); @@ -375,6 +437,7 @@ walker _subgraph { has action_label: str = ""; has agent_node: GraphNode = None; has frame_node: Frame = None; + has interaction_node: Interaction = None; obj __specs__ { static has private:bool = True; @@ -389,7 +452,7 @@ walker _subgraph { can on_state with State entry { if here.touch(self) and here.enabled { # execute the action - response = here.run(self); + response = here.run(self, interaction_node=self.interaction_node); if(type(response) is bool) { visit [-->] else { disengage; @@ -410,7 +473,7 @@ walker _subgraph { can on_completed_state with CompletedState entry { if here.touch(self) { # execute the action - response=here.run(self); + response = here.run(self, interaction_node=self.interaction_node); if response == True { visit[-->](`?ConfirmedState) else { disengage; @@ -428,7 +491,7 @@ walker _subgraph { } } can on_confirmed_state with ConfirmedState entry{ - response = here.run(self); + response = here.run(self, interaction_node=self.interaction_node); self.set_directives(response); } def set_directives(directive:str){ @@ -437,7 +500,7 @@ walker _subgraph { can on_revision_state with RevisionState entry{ if here.touch(self) { # execute the action - response=here.run(self); + response = here.run(self, interaction_node=self.interaction_node); if response is True { visit [<--](`?CompletedState) else { disengage; diff --git a/jvcli/jvcli/templates/2.1.23/project/README.md b/jvcli/jvcli/templates/2.1.24/project/README.md similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/README.md rename to jvcli/jvcli/templates/2.1.24/project/README.md diff --git a/jvcli/jvcli/templates/2.1.23/project/actions/README.md b/jvcli/jvcli/templates/2.1.24/project/actions/README.md similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/actions/README.md rename to jvcli/jvcli/templates/2.1.24/project/actions/README.md diff --git a/jvcli/jvcli/templates/2.1.23/project/daf/README.md b/jvcli/jvcli/templates/2.1.24/project/daf/README.md similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/daf/README.md rename to jvcli/jvcli/templates/2.1.24/project/daf/README.md diff --git a/jvcli/jvcli/templates/2.1.23/project/env.example b/jvcli/jvcli/templates/2.1.24/project/env.example similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/env.example rename to jvcli/jvcli/templates/2.1.24/project/env.example diff --git a/jvcli/jvcli/templates/2.1.23/project/gitignore.example b/jvcli/jvcli/templates/2.1.24/project/gitignore.example similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/gitignore.example rename to jvcli/jvcli/templates/2.1.24/project/gitignore.example diff --git a/jvcli/jvcli/templates/2.1.23/project/globals.jac b/jvcli/jvcli/templates/2.1.24/project/globals.jac similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/globals.jac rename to jvcli/jvcli/templates/2.1.24/project/globals.jac diff --git a/jvcli/jvcli/templates/2.1.23/project/main.jac b/jvcli/jvcli/templates/2.1.24/project/main.jac similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/main.jac rename to jvcli/jvcli/templates/2.1.24/project/main.jac diff --git a/jvcli/jvcli/templates/2.1.23/project/tests/README.md b/jvcli/jvcli/templates/2.1.24/project/tests/README.md similarity index 100% rename from jvcli/jvcli/templates/2.1.23/project/tests/README.md rename to jvcli/jvcli/templates/2.1.24/project/tests/README.md diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/CHANGELOG.md b/jvcli/jvcli/templates/2.1.24/sourcefiles/CHANGELOG.md similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/CHANGELOG.md rename to jvcli/jvcli/templates/2.1.24/sourcefiles/CHANGELOG.md diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/README.md b/jvcli/jvcli/templates/2.1.24/sourcefiles/README.md similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/README.md rename to jvcli/jvcli/templates/2.1.24/sourcefiles/README.md diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/action_app.py b/jvcli/jvcli/templates/2.1.24/sourcefiles/action_app.py similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/action_app.py rename to jvcli/jvcli/templates/2.1.24/sourcefiles/action_app.py diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/action_archetype.jac b/jvcli/jvcli/templates/2.1.24/sourcefiles/action_archetype.jac similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/action_archetype.jac rename to jvcli/jvcli/templates/2.1.24/sourcefiles/action_archetype.jac diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/action_info.yaml b/jvcli/jvcli/templates/2.1.24/sourcefiles/action_info.yaml similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/action_info.yaml rename to jvcli/jvcli/templates/2.1.24/sourcefiles/action_info.yaml diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/action_lib.jac b/jvcli/jvcli/templates/2.1.24/sourcefiles/action_lib.jac similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/action_lib.jac rename to jvcli/jvcli/templates/2.1.24/sourcefiles/action_lib.jac diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/agent_descriptor.yaml b/jvcli/jvcli/templates/2.1.24/sourcefiles/agent_descriptor.yaml similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/agent_descriptor.yaml rename to jvcli/jvcli/templates/2.1.24/sourcefiles/agent_descriptor.yaml diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/agent_info.yaml b/jvcli/jvcli/templates/2.1.24/sourcefiles/agent_info.yaml similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/agent_info.yaml rename to jvcli/jvcli/templates/2.1.24/sourcefiles/agent_info.yaml diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/agent_knowledge.yaml b/jvcli/jvcli/templates/2.1.24/sourcefiles/agent_knowledge.yaml similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/agent_knowledge.yaml rename to jvcli/jvcli/templates/2.1.24/sourcefiles/agent_knowledge.yaml diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/agent_memory.yaml b/jvcli/jvcli/templates/2.1.24/sourcefiles/agent_memory.yaml similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/agent_memory.yaml rename to jvcli/jvcli/templates/2.1.24/sourcefiles/agent_memory.yaml diff --git a/jvcli/jvcli/templates/2.1.23/sourcefiles/interact_action_archetype.jac b/jvcli/jvcli/templates/2.1.24/sourcefiles/interact_action_archetype.jac similarity index 100% rename from jvcli/jvcli/templates/2.1.23/sourcefiles/interact_action_archetype.jac rename to jvcli/jvcli/templates/2.1.24/sourcefiles/interact_action_archetype.jac diff --git a/jvserve/setup.py b/jvserve/setup.py index 2d2f876..edbbbb3 100644 --- a/jvserve/setup.py +++ b/jvserve/setup.py @@ -49,6 +49,7 @@ def get_version() -> str: "aiohttp==3.13.1", "schedule==1.2.2", "boto3==1.40.59", + "sniffio", ], extras_require={ "dev": [