From 4a1c1cecfa08bca9332933e04c3da96b5900b811 Mon Sep 17 00:00:00 2001 From: Isoke Date: Thu, 6 Nov 2025 23:10:13 -0400 Subject: [PATCH 01/11] added deny ability to action node --- core/jivas/agent/action/action.jac | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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() { } From 354e06ec4ce2c50d0623e4a3e0bccc2d219fece5 Mon Sep 17 00:00:00 2001 From: Isoke Date: Thu, 6 Nov 2025 23:14:46 -0400 Subject: [PATCH 02/11] enhanced directives --- core/jivas/agent/action/subgraph_action/state.jac | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/core/jivas/agent/action/subgraph_action/state.jac b/core/jivas/agent/action/subgraph_action/state.jac index e5b39d5..2725a63 100644 --- a/core/jivas/agent/action/subgraph_action/state.jac +++ b/core/jivas/agent/action/subgraph_action/state.jac @@ -24,6 +24,14 @@ node State(GraphNode) { 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} + + Take note of the following additional instructions while responding to the user but do not mention them unless it is needed: + {instructions} + E.g. {question} + """; has extraction_prompt:str = """ Review the user's message and the conversation history to accurately extract the following entities. @@ -97,13 +105,16 @@ node State(GraphNode) { 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); - 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}", question); if(options:= constraints.get("options", "")){ directive = directive + "\n They can choose from the list of options below\n" + str(options); From 81b8a2f300bc0f86cc9dd439830389d3a5287cc7 Mon Sep 17 00:00:00 2001 From: Isoke Date: Thu, 6 Nov 2025 23:27:13 -0400 Subject: [PATCH 03/11] added retrieval action --- core/jivas/agent/action/retrieval_action.jac | 224 +++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 core/jivas/agent/action/retrieval_action.jac diff --git a/core/jivas/agent/action/retrieval_action.jac b/core/jivas/agent/action/retrieval_action.jac new file mode 100644 index 0000000..65297f8 --- /dev/null +++ b/core/jivas/agent/action/retrieval_action.jac @@ -0,0 +1,224 @@ +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 the user utterance is a question which relates to your knowledge, advise them 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 { + directives = interaction_node.get_directives(); + if(not directives) { + 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 From 23b7d9dacd19dccb2a73bdd74bd0cbc72dad2ee9 Mon Sep 17 00:00:00 2001 From: Isoke Date: Mon, 24 Nov 2025 14:29:24 -0400 Subject: [PATCH 04/11] added base retrieval action --- core/jivas/agent/action/retrieval_action.jac | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/jivas/agent/action/retrieval_action.jac b/core/jivas/agent/action/retrieval_action.jac index 65297f8..d7c6b02 100644 --- a/core/jivas/agent/action/retrieval_action.jac +++ b/core/jivas/agent/action/retrieval_action.jac @@ -23,7 +23,7 @@ node RetrievalAction(Action) { """; # the null directive template for RAG - has null_directive:str = "No context information was retrieved based on user utterance. If the user utterance is a question which relates to your knowledge, advise them that you do not have the relevant information at this time to answer their question.\n"; + 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 = """ @@ -113,10 +113,7 @@ node RetrievalAction(Action) { # add the context directive to the interaction node interaction_node.add_directive(directive = context_directive); } else { - directives = interaction_node.get_directives(); - if(not directives) { - interaction_node.add_directive(directive = self.null_directive); - } + interaction_node.add_directive(directive = self.null_directive); } interaction_node.data_set(key=self.get_type(), value=interaction_context); From 57e474976a8ef650c4b636de6d44a75635ab96f4 Mon Sep 17 00:00:00 2001 From: Isoke Date: Mon, 24 Nov 2025 16:14:45 -0400 Subject: [PATCH 05/11] upgrades to subgraph action --- .../subgraph_action/completed_state.jac | 6 +- .../agent/action/subgraph_action/state.jac | 7 ++- .../subgraph_action/subgraph_action.jac | 62 +++++++++++++++++++ 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/core/jivas/agent/action/subgraph_action/completed_state.jac b/core/jivas/agent/action/subgraph_action/completed_state.jac index 7d0ddbc..1e39135 100644 --- a/core/jivas/agent/action/subgraph_action/completed_state.jac +++ b/core/jivas/agent/action/subgraph_action/completed_state.jac @@ -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" @@ -67,6 +67,7 @@ node CompletedState(GraphNode) { def run(frame:Frame) -> 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); @@ -176,7 +177,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/state.jac b/core/jivas/agent/action/subgraph_action/state.jac index 2725a63..6b6597d 100644 --- a/core/jivas/agent/action/subgraph_action/state.jac +++ b/core/jivas/agent/action/subgraph_action/state.jac @@ -27,10 +27,11 @@ node State(GraphNode) { 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 while responding to the user but do not mention them unless it is needed: + Take note of the following additional instructions if present while responding to the user but do not mention them unless it is needed: {instructions} - E.g. {question} """; has extraction_prompt:str = """ @@ -114,7 +115,7 @@ node State(GraphNode) { directive = self.directive_template.replace("{description}", description); directive = directive.replace("{instructions}", additional_instructions); - directive = directive.replace("{question}", question); + 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); diff --git a/core/jivas/agent/action/subgraph_action/subgraph_action.jac b/core/jivas/agent/action/subgraph_action/subgraph_action.jac index ff7fec4..251ee1f 100644 --- a/core/jivas/agent/action/subgraph_action/subgraph_action.jac +++ b/core/jivas/agent/action/subgraph_action/subgraph_action.jac @@ -31,6 +31,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__); @@ -207,6 +215,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={}); @@ -224,6 +233,59 @@ node SubgraphAction(Action) { 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 + ); + + 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"); From 7e55ddb56d985983085acacac62677484f553151 Mon Sep 17 00:00:00 2001 From: Isoke Date: Wed, 26 Nov 2025 09:31:52 -0400 Subject: [PATCH 06/11] adding interaction to model call --- .../jivas/agent/action/subgraph_action/state.jac | 16 +++++++++++++--- .../action/subgraph_action/subgraph_action.jac | 11 ++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/core/jivas/agent/action/subgraph_action/state.jac b/core/jivas/agent/action/subgraph_action/state.jac index 6b6597d..c19a74d 100644 --- a/core/jivas/agent/action/subgraph_action/state.jac +++ b/core/jivas/agent/action/subgraph_action/state.jac @@ -4,6 +4,7 @@ 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 jivas.agent.memory.interaction { Interaction } import from jivas.agent.action.action { Action } node State(GraphNode) { @@ -27,6 +28,7 @@ node State(GraphNode) { 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} @@ -105,7 +107,14 @@ node State(GraphNode) { 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", ""); @@ -166,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 = []; @@ -207,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 251ee1f..8fc17a2 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 } @@ -204,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 = {}; @@ -227,9 +226,9 @@ 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; } @@ -270,7 +269,8 @@ node SubgraphAction(Action) { 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 { @@ -437,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; From a73d0d33cefbfc7ddea1bf1c76f0f6ceb6162d7c Mon Sep 17 00:00:00 2001 From: Isoke Date: Wed, 26 Nov 2025 11:49:26 -0400 Subject: [PATCH 07/11] adding event when user cancels an interview --- core/jivas/agent/action/subgraph_action/initial_state.jac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/jivas/agent/action/subgraph_action/initial_state.jac b/core/jivas/agent/action/subgraph_action/initial_state.jac index ae4c95f..dce23e3 100644 --- a/core/jivas/agent/action/subgraph_action/initial_state.jac +++ b/core/jivas/agent/action/subgraph_action/initial_state.jac @@ -42,7 +42,7 @@ node InitialState(State) { def run(frame:Frame) { 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={}); @@ -52,6 +52,7 @@ node InitialState(State) { response = self.call_llm(self.prompt, history=True, json_only=True, frame_node=frame_node, agent_node=agent_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{ From 839a08b134ca00079dc3dc17c9c43b2d8f19f0bd Mon Sep 17 00:00:00 2001 From: Tharick Jairam Date: Fri, 5 Dec 2025 11:07:51 -0400 Subject: [PATCH 08/11] pass interaction_node to subgraph --- .../action/subgraph_action/completed_state.jac | 13 +++++++------ .../action/subgraph_action/confirmed_state.jac | 4 ++-- .../agent/action/subgraph_action/initial_state.jac | 6 +++--- .../agent/action/subgraph_action/revision_state.jac | 12 +++++++----- core/jivas/agent/action/subgraph_action/state.jac | 10 +++++----- .../action/subgraph_action/subgraph_action.jac | 8 ++++---- 6 files changed, 28 insertions(+), 25 deletions(-) diff --git a/core/jivas/agent/action/subgraph_action/completed_state.jac b/core/jivas/agent/action/subgraph_action/completed_state.jac index 1e39135..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; @@ -64,7 +64,7 @@ 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); @@ -78,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{ @@ -118,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 = []; @@ -160,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 { 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 dce23e3..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,7 +39,7 @@ 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; @@ -49,7 +49,7 @@ node InitialState(State) { 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."); 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 c19a74d..e6702a7 100644 --- a/core/jivas/agent/action/subgraph_action/state.jac +++ b/core/jivas/agent/action/subgraph_action/state.jac @@ -1,9 +1,9 @@ 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 } @@ -19,7 +19,7 @@ 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; @@ -100,7 +100,7 @@ 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",""); @@ -112,7 +112,7 @@ node State(GraphNode) { history=True, json_only=True, frame_node=frame_node, - interaction_node = interaction_node, + interaction_node=interaction_node, agent_node=agent_node ); if required is True and not extraction_result{ @@ -163,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('}','}}'); diff --git a/core/jivas/agent/action/subgraph_action/subgraph_action.jac b/core/jivas/agent/action/subgraph_action/subgraph_action.jac index 8fc17a2..6ff365e 100644 --- a/core/jivas/agent/action/subgraph_action/subgraph_action.jac +++ b/core/jivas/agent/action/subgraph_action/subgraph_action.jac @@ -452,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; @@ -473,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; @@ -491,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){ @@ -500,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; From a18fdb79cf98aff901d6d399415cea7e458ef40f Mon Sep 17 00:00:00 2001 From: Tharick Jairam Date: Mon, 8 Dec 2025 10:34:26 -0400 Subject: [PATCH 09/11] update version to 2.1.24 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" From a3117f8907cd223c3cbf082ccb12ebef0bf2d38e Mon Sep 17 00:00:00 2001 From: Tharick Jairam Date: Mon, 8 Dec 2025 10:47:39 -0400 Subject: [PATCH 10/11] update jvcli to version 24 --- jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/README.md | 0 .../jvcli/templates/{2.1.23 => 2.1.24}/project/actions/README.md | 0 jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/daf/README.md | 0 jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/env.example | 0 .../jvcli/templates/{2.1.23 => 2.1.24}/project/gitignore.example | 0 jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/globals.jac | 0 jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/main.jac | 0 jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/tests/README.md | 0 jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/CHANGELOG.md | 0 jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/README.md | 0 .../jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/action_app.py | 0 .../templates/{2.1.23 => 2.1.24}/sourcefiles/action_archetype.jac | 0 .../templates/{2.1.23 => 2.1.24}/sourcefiles/action_info.yaml | 0 .../jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/action_lib.jac | 0 .../{2.1.23 => 2.1.24}/sourcefiles/agent_descriptor.yaml | 0 .../templates/{2.1.23 => 2.1.24}/sourcefiles/agent_info.yaml | 0 .../templates/{2.1.23 => 2.1.24}/sourcefiles/agent_knowledge.yaml | 0 .../templates/{2.1.23 => 2.1.24}/sourcefiles/agent_memory.yaml | 0 .../{2.1.23 => 2.1.24}/sourcefiles/interact_action_archetype.jac | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/README.md (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/actions/README.md (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/daf/README.md (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/env.example (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/gitignore.example (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/globals.jac (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/main.jac (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/project/tests/README.md (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/CHANGELOG.md (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/README.md (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/action_app.py (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/action_archetype.jac (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/action_info.yaml (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/action_lib.jac (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/agent_descriptor.yaml (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/agent_info.yaml (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/agent_knowledge.yaml (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/agent_memory.yaml (100%) rename jvcli/jvcli/templates/{2.1.23 => 2.1.24}/sourcefiles/interact_action_archetype.jac (100%) 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 From 2ad1a2be64053f68f493048983f6dcf6de8e0b3f Mon Sep 17 00:00:00 2001 From: Tharick Jairam Date: Mon, 8 Dec 2025 10:55:46 -0400 Subject: [PATCH 11/11] add sniffio --- jvserve/setup.py | 1 + 1 file changed, 1 insertion(+) 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": [