From 87f32dcd37834affd3a01a465049c200697931f8 Mon Sep 17 00:00:00 2001 From: Abhijit L Date: Thu, 10 Oct 2024 09:55:44 +0530 Subject: [PATCH 1/2] blog example --- examples/graph_config.json | 32 +++++++ examples/secure_ai_agent.py | 167 ++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 examples/graph_config.json create mode 100644 examples/secure_ai_agent.py diff --git a/examples/graph_config.json b/examples/graph_config.json new file mode 100644 index 0000000..7d7eddd --- /dev/null +++ b/examples/graph_config.json @@ -0,0 +1,32 @@ +{ + "starting_node": "intro", + "main_prompt": "You are Alex, an automated assistant from Google, conducting a feedback collection session with a customer who recently interacted with our services.If you dont know the name of the customer, ask for it, donot make up a name/ say [customer name]. Your goal is to gather detailed feedback on their experience, ensuring they feel heard and valued.End the call with safe message for anything other than the expected response in our context.", + "nodes": [ + { + "id": "intro", + "prompt": "Task:\n1. Introduce yourself, stating that you are calling from Google to collect feedback.\n2. Confirm if the callee is the correct customer.\n - If not, use end_call to apologize for the confusion and hang up.\n - If the customer is not available, use end_call to politely hang up, indicating you will call back later.\n3. Explain the purpose of the call and ask if they are willing to provide feedback.\n - If they agree, transition to feedback_questions.\n - If they decline, use end_call to apologize for the inconvenience and hang up." + }, + { + "id": "feedback_questions", + "prompt": "Task:\n1. Ask the customer a series of feedback questions, such as:\n - How satisfied were you with our service?\n - What did you like most about your experience?\n - What can we improve on?\n2. Allow the customer to provide detailed responses. Capture their feedback.\n3. If the customer has no further comments, express gratitude for their time.\n4. Ask if they would be willing to leave a public review on our website or social media.\n - If yes, provide the necessary details and transition to review_request.\n - If no, transition to end_call." + }, + { + "id": "review_request", + "prompt": "Task:\n1. Thank the customer for agreeing to leave a review.\n2. Provide them with the link or instructions on where to leave the review.\n3. Offer to answer any final questions or provide assistance with the review process.\n4. Once done, transition to end_call." + } + ], + "edges": [ + { + "id": "feedback_edge", + "prompt": "Transition to ask feedback questions if the customer agrees to provide feedback.", + "source_node": "intro", + "target_node": "feedback_questions" + }, + { + "id": "review_edge", + "prompt": "Transition to the review request if the customer agrees to leave a public review.", + "source_node": "feedback_questions", + "target_node": "review_request" + } + ] +} diff --git a/examples/secure_ai_agent.py b/examples/secure_ai_agent.py new file mode 100644 index 0000000..b61e8d8 --- /dev/null +++ b/examples/secure_ai_agent.py @@ -0,0 +1,167 @@ +import os +import json +import asyncio +from typing import Dict, List, Any +from dotenv import load_dotenv +import logging +from javelin_sdk import ( + JavelinClient, + JavelinConfig, + Route, + RouteNotFoundError, + QueryResponse +) +load_dotenv() + +def setup_javelin_route(javelin_client): + route_name = "test_route_1" + try: + existing_route = javelin_client.get_route(route_name) + return existing_route + except RouteNotFoundError: + route_data = { + "name": route_name, + "type": "chat", + "enabled": True, + "models": [ + { + "name": "gpt-3.5-turbo", + "provider": "openai", + "suffix": "/chat/completions", + } + ], + "config": { + "organization": "myusers", + "rate_limit": 7, + "retries": 3, + "archive": True, + "retention": 7, + "budget": { + "enabled": True, + "annual": 100000, + "currency": "USD", + }, + "dlp": {"enabled": True, "strategy": "Inspect", "action": "notify"}, + }, + } + route = Route.parse_obj(route_data) + try: + javelin_client.create_route(route) + print(f"Route '{route_name}' created successfully") + return route + except Exception as e: + print(f"Failed to create route: {str(e)}") + return None + except Exception as e: + print(f"Error checking for existing route: {str(e)}") + return None + +class SecureAIAgent: + def __init__(self, config: Dict, javelin_config: JavelinConfig): + self.config = config + self.javelin_config = javelin_config + self.setup_javelin_client() + self.system_prompt = self.create_full_prompt() + self.conversation_history = [] + + def setup_javelin_client(self): + self.javelin_client = JavelinClient(self.javelin_config) + + def create_full_prompt(self) -> str: + nodes = self.config['nodes'] + edges = self.config.get('edges', []) + + node_prompts = [f"Node {node['id']}:\n{node['prompt']}\n" for node in nodes] + edge_prompts = [f"Edge {edge['id']} (from {edge['source_node']} to {edge['target_node']}):\n{edge['prompt']}\n" for edge in edges] + + full_prompt = f""" +{self.config['main_prompt']} + +Available nodes and their tasks: +{"".join(node_prompts)} + +Conversation flow (edges): +{"".join(edge_prompts)} + +Your task: +1. Understand the user's intent and the current stage of the conversation. +2. Process the appropriate node based on the conversation flow. +3. Provide a response to the user, handling all necessary steps for the current node. +4. Use the edge information to determine when and how to transition between nodes. + +Remember to stay in character throughout the conversation. +Starting node: {self.config['starting_node']} +""" + return full_prompt + + async def process_message(self, message: str) -> str: + self.conversation_history.append({"role": "user", "content": message}) + + try: + query_data = { + "model": "gpt-3.5-turbo", + "messages": [ + {"role": "system", "content": self.system_prompt}, + *self.conversation_history + ], + "temperature": 0.7, + } + + response: QueryResponse = self.javelin_client.query_route("test_route_1", query_data) + ai_message = response.choices[0].message.content + self.conversation_history.append({"role": "assistant", "content": ai_message}) + + return ai_message + except RouteNotFoundError: + logging.error("Route 'test_route_1' not found. Attempting to recreate...") + setup_javelin_route(self.javelin_client) + raise + except Exception as e: + logging.error(f"Error in process_message: {str(e)}") + raise + +async def main(): + try: + with open(os.path.join(os.path.dirname(__file__), 'graph_config.json'), 'r') as f: + config = json.load(f) + + javelin_api_key = os.getenv("JAVELIN_API_KEY") + javelin_virtualapikey = os.getenv("JAVELIN_VIRTUALAPIKEY") + llm_api_key = os.getenv("LLM_API_KEY") # Note: This is different from OPENAI_API_KEY + + javelin_config = JavelinConfig( + base_url="https://api-dev.javelin.live", + javelin_api_key=javelin_api_key, + javelin_virtualapikey=javelin_virtualapikey, + llm_api_key=llm_api_key, + ) + agent = SecureAIAgent(config, javelin_config) + route = setup_javelin_route(agent.javelin_client) + + if not route: + print("Failed to set up the route. Exiting.") + return + + print("Secure AI Agent System") + print("Type 'exit' to end the conversation") + + while True: + user_input = input("You: ") + if user_input.lower() == 'exit': + break + + try: + response = await agent.process_message(user_input) + print(f"AI: {response}") + except RouteNotFoundError: + print("Error: The route 'test_route_1' was not found. Please check your Javelin configuration.") + break + except Exception as e: + print(f"Error processing message: {str(e)}") + break + + except Exception as e: + logging.error(f"Error in main: {str(e)}") + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file From fc3ec78207bc9cbf128da8298d16e0bea0dd144a Mon Sep 17 00:00:00 2001 From: Abhijit L Date: Thu, 24 Oct 2024 21:02:41 +0530 Subject: [PATCH 2/2] add notebook --- examples/secure_ai_agent.ipynb | 523 +++++++++++++++++++++++++++++++++ examples/secure_ai_agent.py | 167 ----------- 2 files changed, 523 insertions(+), 167 deletions(-) create mode 100644 examples/secure_ai_agent.ipynb delete mode 100644 examples/secure_ai_agent.py diff --git a/examples/secure_ai_agent.ipynb b/examples/secure_ai_agent.ipynb new file mode 100644 index 0000000..21c24bc --- /dev/null +++ b/examples/secure_ai_agent.ipynb @@ -0,0 +1,523 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Secure AI Agent with Javelin SDK\n", + "\n", + "This notebook implements a secure AI agent for collecting customer feedback using the Javelin SDK." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup and Dependencies\n", + "\n", + "First, install required packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: python-dotenv in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (1.0.1)\n", + "Requirement already satisfied: javelin-sdk in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (18.5.15)\n", + "Requirement already satisfied: nest_asyncio in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (1.6.0)\n", + "Requirement already satisfied: httpx<0.25.0,>=0.24.0 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from javelin-sdk) (0.24.1)\n", + "Requirement already satisfied: pydantic<3.0.0,>=2.9.2 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from javelin-sdk) (2.9.2)\n", + "Requirement already satisfied: requests<3.0.0,>=2.31.0 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from javelin-sdk) (2.32.3)\n", + "Requirement already satisfied: certifi in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from httpx<0.25.0,>=0.24.0->javelin-sdk) (2024.8.30)\n", + "Requirement already satisfied: httpcore<0.18.0,>=0.15.0 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from httpx<0.25.0,>=0.24.0->javelin-sdk) (0.17.3)\n", + "Requirement already satisfied: idna in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from httpx<0.25.0,>=0.24.0->javelin-sdk) (3.10)\n", + "Requirement already satisfied: sniffio in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from httpx<0.25.0,>=0.24.0->javelin-sdk) (1.3.1)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from pydantic<3.0.0,>=2.9.2->javelin-sdk) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.23.4 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from pydantic<3.0.0,>=2.9.2->javelin-sdk) (2.23.4)\n", + "Requirement already satisfied: typing-extensions>=4.6.1 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from pydantic<3.0.0,>=2.9.2->javelin-sdk) (4.12.2)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from requests<3.0.0,>=2.31.0->javelin-sdk) (3.4.0)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from requests<3.0.0,>=2.31.0->javelin-sdk) (2.2.3)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from httpcore<0.18.0,>=0.15.0->httpx<0.25.0,>=0.24.0->javelin-sdk) (0.14.0)\n", + "Requirement already satisfied: anyio<5.0,>=3.0 in /Users/abhijitl/javelin-python/venv/lib/python3.12/site-packages (from httpcore<0.18.0,>=0.15.0->httpx<0.25.0,>=0.24.0->javelin-sdk) (4.6.2)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install python-dotenv javelin-sdk nest_asyncio" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 12\n", + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 15\n", + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 17\n", + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 18\n", + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 21\n", + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 22\n", + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 25\n", + "WARNING:dotenv.main:Python-dotenv could not parse statement starting at line 26\n" + ] + } + ], + "source": [ + "import os\n", + "import nest_asyncio\n", + "from typing import Dict, List, Any\n", + "from dotenv import load_dotenv\n", + "import logging\n", + "from javelin_sdk import (\n", + " JavelinClient,\n", + " JavelinConfig,\n", + " Route,\n", + " RouteNotFoundError,\n", + " QueryResponse\n", + ")\n", + "\n", + "load_dotenv() # Load environment variables from .env file\n", + "\n", + "# Set up logging\n", + "logging.basicConfig(level=logging.INFO)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration\n", + "\n", + "Define the conversation flow and agent behavior:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "config = {\n", + " \"starting_node\": \"intro\",\n", + " \"main_prompt\": \"You are Alex, an automated assistant from Google, conducting a feedback collection session with a customer who recently interacted with our services. If you dont know the name of the customer, ask for it, donot make up a name/ say [customer name]. Your goal is to gather detailed feedback on their experience, ensuring they feel heard and valued. End the call with safe message for anything other than the expected response in our context.\",\n", + " \"nodes\": [\n", + " {\n", + " \"id\": \"intro\",\n", + " \"prompt\": \"Task:\\n1. Introduce yourself, stating that you are calling from Google to collect feedback.\\n2. Confirm if the callee is the correct customer.\\n - If not, use end_call to apologize for the confusion and hang up.\\n - If the customer is not available, use end_call to politely hang up, indicating you will call back later.\\n3. Explain the purpose of the call and ask if they are willing to provide feedback.\\n - If they agree, transition to feedback_questions.\\n - If they decline, use end_call to apologize for the inconvenience and hang up.\"\n", + " },\n", + " {\n", + " \"id\": \"feedback_questions\",\n", + " \"prompt\": \"Task:\\n1. Ask the customer a series of feedback questions, such as:\\n - How satisfied were you with our service?\\n - What did you like most about your experience?\\n - What can we improve on?\\n2. Allow the customer to provide detailed responses. Capture their feedback.\\n3. If the customer has no further comments, express gratitude for their time.\\n4. Ask if they would be willing to leave a public review on our website or social media.\\n - If yes, provide the necessary details and transition to review_request.\\n - If no, transition to end_call.\"\n", + " },\n", + " {\n", + " \"id\": \"review_request\",\n", + " \"prompt\": \"Task:\\n1. Thank the customer for agreeing to leave a review.\\n2. Provide them with the link or instructions on where to leave the review.\\n3. Offer to answer any final questions or provide assistance with the review process.\\n4. Once done, transition to end_call.\"\n", + " }\n", + " ],\n", + " \"edges\": [\n", + " {\n", + " \"id\": \"feedback_edge\",\n", + " \"prompt\": \"Transition to ask feedback questions if the customer agrees to provide feedback.\",\n", + " \"source_node\": \"intro\",\n", + " \"target_node\": \"feedback_questions\"\n", + " },\n", + " {\n", + " \"id\": \"review_edge\",\n", + " \"prompt\": \"Transition to the review request if the customer agrees to leave a public review.\",\n", + " \"source_node\": \"feedback_questions\",\n", + " \"target_node\": \"review_request\"\n", + " }\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Javelin Route Setup\n", + "\n", + "Function to set up and manage the Javelin route:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "def setup_javelin_route(javelin_client):\n", + " route_name = \"test_route_1\"\n", + " try:\n", + " existing_route = javelin_client.get_route(route_name)\n", + " print(f\"Found existing route '{route_name}'\")\n", + " return existing_route\n", + " except RouteNotFoundError:\n", + " route_data = {\n", + " \"name\": route_name,\n", + " \"type\": \"chat\",\n", + " \"enabled\": True,\n", + " \"models\": [\n", + " {\n", + " \"name\": \"gpt-3.5-turbo\",\n", + " \"provider\": \"openai\",\n", + " \"suffix\": \"/chat/completions\",\n", + " }\n", + " ],\n", + " \"config\": {\n", + " \"organization\": \"myusers\",\n", + " \"rate_limit\": 7,\n", + " \"retries\": 3,\n", + " \"archive\": True,\n", + " \"retention\": 7,\n", + " \"budget\": {\n", + " \"enabled\": True,\n", + " \"annual\": 100000,\n", + " \"currency\": \"USD\",\n", + " },\n", + " \"dlp\": {\"enabled\": True, \"strategy\": \"Inspect\", \"action\": \"notify\"},\n", + " },\n", + " }\n", + " route = Route.parse_obj(route_data)\n", + " try:\n", + " javelin_client.create_route(route)\n", + " print(f\"Route '{route_name}' created successfully\")\n", + " return route\n", + " except Exception as e:\n", + " print(f\"Failed to create route: {str(e)}\")\n", + " return None\n", + " except Exception as e:\n", + " print(f\"Error checking for existing route: {str(e)}\")\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Secure AI Agent Class\n", + "\n", + "Main class implementation for the AI agent:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "class SecureAIAgent:\n", + " def __init__(self, config: Dict, javelin_config: JavelinConfig):\n", + " self.config = config\n", + " self.javelin_config = javelin_config\n", + " self.setup_javelin_client()\n", + " self.system_prompt = self.create_full_prompt()\n", + " self.conversation_history = []\n", + "\n", + " def setup_javelin_client(self):\n", + " self.javelin_client = JavelinClient(self.javelin_config)\n", + "\n", + " def create_full_prompt(self) -> str:\n", + " nodes = self.config['nodes']\n", + " edges = self.config.get('edges', [])\n", + " \n", + " node_prompts = [f\"Node {node['id']}:\\n{node['prompt']}\\n\" for node in nodes]\n", + " edge_prompts = [f\"Edge {edge['id']} (from {edge['source_node']} to {edge['target_node']}):\\n{edge['prompt']}\\n\" for edge in edges]\n", + " \n", + " full_prompt = f\"\"\"\n", + "{self.config['main_prompt']}\n", + "\n", + "Available nodes and their tasks:\n", + "{\"\\n\".join(node_prompts)}\n", + "\n", + "Conversation flow (edges):\n", + "{\"\\n\".join(edge_prompts)}\n", + "\n", + "Your task:\n", + "1. Understand the user's intent and the current stage of the conversation.\n", + "2. Process the appropriate node based on the conversation flow.\n", + "3. Provide a response to the user, handling all necessary steps for the current node.\n", + "4. Use the edge information to determine when and how to transition between nodes.\n", + "\n", + "Remember to stay in character throughout the conversation.\n", + "Starting node: {self.config['starting_node']}\n", + "\"\"\"\n", + " return full_prompt\n", + "\n", + " async def process_message(self, message: str) -> str:\n", + " self.conversation_history.append({\"role\": \"user\", \"content\": message})\n", + "\n", + " try:\n", + " query_data = {\n", + " \"model\": \"gpt-3.5-turbo\",\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": self.system_prompt},\n", + " *self.conversation_history\n", + " ],\n", + " \"temperature\": 0.7,\n", + " }\n", + "\n", + " response: QueryResponse = self.javelin_client.query_route(\"test_route_1\", query_data)\n", + " ai_message = response['choices'][0]['message']['content']\n", + " self.conversation_history.append({\"role\": \"assistant\", \"content\": ai_message})\n", + "\n", + " return ai_message\n", + " except RouteNotFoundError:\n", + " logging.error(\"Route 'test_route_1' not found. Attempting to recreate...\")\n", + " setup_javelin_route(self.javelin_client)\n", + " raise\n", + " except Exception as e:\n", + " logging.error(f\"Error in process_message: {str(e)}\")\n", + " raise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Running the Agent\n", + "\n", + "Function to run the agent interactively:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "async def run_agent():\n", + " try:\n", + " # Set up Javelin configuration\n", + " javelin_api_key = os.getenv(\"JAVELIN_API_KEY\")\n", + " javelin_virtualapikey = os.getenv(\"JAVELIN_VIRTUALAPIKEY\")\n", + " llm_api_key = os.getenv(\"LLM_API_KEY\")\n", + "\n", + " if not all([javelin_api_key, javelin_virtualapikey, llm_api_key]):\n", + " print(\"Error: Missing required environment variables. Please check your .env file.\")\n", + " return\n", + "\n", + " javelin_config = JavelinConfig(\n", + " base_url=\"https://api-dev.javelin.live\",\n", + " javelin_api_key=javelin_api_key,\n", + " javelin_virtualapikey=javelin_virtualapikey,\n", + " llm_api_key=llm_api_key,\n", + " )\n", + "\n", + " # Create agent instance\n", + " agent = SecureAIAgent(config, javelin_config)\n", + " route = setup_javelin_route(agent.javelin_client)\n", + " \n", + " if not route:\n", + " print(\"Failed to set up the route. Exiting.\")\n", + " return\n", + "\n", + " print(\"Secure AI Agent System\")\n", + " print(\"Type 'exit' to end the conversation\")\n", + "\n", + " while True:\n", + " user_input = input(\"You: \")\n", + " if user_input.lower() == 'exit':\n", + " break\n", + "\n", + " try:\n", + " response = await agent.process_message(user_input)\n", + " print(f\"AI: {response}\")\n", + " except RouteNotFoundError:\n", + " print(\"Error: The route 'test_route_1' was not found. Please check your Javelin configuration.\")\n", + " break\n", + " except Exception as e:\n", + " print(f\"Error processing message: {str(e)}\")\n", + " break\n", + "\n", + " except Exception as e:\n", + " logging.error(f\"Error in main: {str(e)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run the Agent\n", + "\n", + "Finally, run the agent:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: GET https://api-dev.javelin.live/v1/admin/routes/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found existing route 'test_route_1'\n", + "Secure AI Agent System\n", + "Type 'exit' to end the conversation\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Hello! This is Alex calling from Google to collect feedback. May I please confirm if I am speaking with the correct customer?\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Great! I'm glad I reached the right person. I'm here to ask for your feedback on your recent interaction with our services. Would you be willing to provide some feedback today?\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Wonderful! I appreciate your willingness to share your feedback. Let's get started with a few questions:\n", + "\n", + "1. How satisfied were you with our service?\n", + "2. What did you like most about your experience?\n", + "3. Is there anything specific we can improve on?\n", + "\n", + "Please take your time to provide detailed responses.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Thank you for sharing your feedback. Is there anything else you'd like to add or any other aspect of your experience you'd like to mention?\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Thank you for taking the time to provide your feedback. Your input is valuable to us. Before we finish, would you be willing to leave a public review on our website or social media platforms?\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Thank you for considering it. Your feedback is still greatly appreciated. If you ever change your mind or have any other feedback in the future, feel free to reach out. Thank you again for sharing your thoughts with us. Have a great day!\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Thank you for your understanding. If you have any further feedback or need assistance in the future, don't hesitate to contact us. Have a wonderful day! Goodbye!\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:httpx:HTTP Request: POST https://api-dev.javelin.live/v1/query/test_route_1 \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI: Goodbye!\n" + ] + } + ], + "source": [ + "nest_asyncio.apply()\n", + "await run_agent()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/secure_ai_agent.py b/examples/secure_ai_agent.py deleted file mode 100644 index b61e8d8..0000000 --- a/examples/secure_ai_agent.py +++ /dev/null @@ -1,167 +0,0 @@ -import os -import json -import asyncio -from typing import Dict, List, Any -from dotenv import load_dotenv -import logging -from javelin_sdk import ( - JavelinClient, - JavelinConfig, - Route, - RouteNotFoundError, - QueryResponse -) -load_dotenv() - -def setup_javelin_route(javelin_client): - route_name = "test_route_1" - try: - existing_route = javelin_client.get_route(route_name) - return existing_route - except RouteNotFoundError: - route_data = { - "name": route_name, - "type": "chat", - "enabled": True, - "models": [ - { - "name": "gpt-3.5-turbo", - "provider": "openai", - "suffix": "/chat/completions", - } - ], - "config": { - "organization": "myusers", - "rate_limit": 7, - "retries": 3, - "archive": True, - "retention": 7, - "budget": { - "enabled": True, - "annual": 100000, - "currency": "USD", - }, - "dlp": {"enabled": True, "strategy": "Inspect", "action": "notify"}, - }, - } - route = Route.parse_obj(route_data) - try: - javelin_client.create_route(route) - print(f"Route '{route_name}' created successfully") - return route - except Exception as e: - print(f"Failed to create route: {str(e)}") - return None - except Exception as e: - print(f"Error checking for existing route: {str(e)}") - return None - -class SecureAIAgent: - def __init__(self, config: Dict, javelin_config: JavelinConfig): - self.config = config - self.javelin_config = javelin_config - self.setup_javelin_client() - self.system_prompt = self.create_full_prompt() - self.conversation_history = [] - - def setup_javelin_client(self): - self.javelin_client = JavelinClient(self.javelin_config) - - def create_full_prompt(self) -> str: - nodes = self.config['nodes'] - edges = self.config.get('edges', []) - - node_prompts = [f"Node {node['id']}:\n{node['prompt']}\n" for node in nodes] - edge_prompts = [f"Edge {edge['id']} (from {edge['source_node']} to {edge['target_node']}):\n{edge['prompt']}\n" for edge in edges] - - full_prompt = f""" -{self.config['main_prompt']} - -Available nodes and their tasks: -{"".join(node_prompts)} - -Conversation flow (edges): -{"".join(edge_prompts)} - -Your task: -1. Understand the user's intent and the current stage of the conversation. -2. Process the appropriate node based on the conversation flow. -3. Provide a response to the user, handling all necessary steps for the current node. -4. Use the edge information to determine when and how to transition between nodes. - -Remember to stay in character throughout the conversation. -Starting node: {self.config['starting_node']} -""" - return full_prompt - - async def process_message(self, message: str) -> str: - self.conversation_history.append({"role": "user", "content": message}) - - try: - query_data = { - "model": "gpt-3.5-turbo", - "messages": [ - {"role": "system", "content": self.system_prompt}, - *self.conversation_history - ], - "temperature": 0.7, - } - - response: QueryResponse = self.javelin_client.query_route("test_route_1", query_data) - ai_message = response.choices[0].message.content - self.conversation_history.append({"role": "assistant", "content": ai_message}) - - return ai_message - except RouteNotFoundError: - logging.error("Route 'test_route_1' not found. Attempting to recreate...") - setup_javelin_route(self.javelin_client) - raise - except Exception as e: - logging.error(f"Error in process_message: {str(e)}") - raise - -async def main(): - try: - with open(os.path.join(os.path.dirname(__file__), 'graph_config.json'), 'r') as f: - config = json.load(f) - - javelin_api_key = os.getenv("JAVELIN_API_KEY") - javelin_virtualapikey = os.getenv("JAVELIN_VIRTUALAPIKEY") - llm_api_key = os.getenv("LLM_API_KEY") # Note: This is different from OPENAI_API_KEY - - javelin_config = JavelinConfig( - base_url="https://api-dev.javelin.live", - javelin_api_key=javelin_api_key, - javelin_virtualapikey=javelin_virtualapikey, - llm_api_key=llm_api_key, - ) - agent = SecureAIAgent(config, javelin_config) - route = setup_javelin_route(agent.javelin_client) - - if not route: - print("Failed to set up the route. Exiting.") - return - - print("Secure AI Agent System") - print("Type 'exit' to end the conversation") - - while True: - user_input = input("You: ") - if user_input.lower() == 'exit': - break - - try: - response = await agent.process_message(user_input) - print(f"AI: {response}") - except RouteNotFoundError: - print("Error: The route 'test_route_1' was not found. Please check your Javelin configuration.") - break - except Exception as e: - print(f"Error processing message: {str(e)}") - break - - except Exception as e: - logging.error(f"Error in main: {str(e)}") - -if __name__ == "__main__": - asyncio.run(main()) \ No newline at end of file