Repository: UmstadAI/zkAppUmstad (language: python)

Issue:
Eval route api



Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index ec03cd9..7883baf 100644
--- a/README.md
+++ b/README.md
@@ -1,74 +1,94 @@
 # zkApps Umstad AI Assistant
 
 ## Introduction
-zkApps Umstad AI Assistant, your expert guide in the world of MINA Protocol, o1js. Designed to assist developers, this chatbot provides in-depth assistance with zkApps o1js which is specifically designed to assist developers working on zkApps development  powered by the GPT language model. This project includes two main application:
+
+zkApps Umstad AI Assistant, your expert guide in the world of MINA Protocol, o1js. Designed to assist developers, this chatbot provides in-depth assistance with zkApps o1js which is specifically designed to assist developers working on zkApps development powered by the GPT language model. This project includes two main application:
 
 - **Web Application Chatbot:** zkappsumstad.com
 - **CLI Agent:** DOWNLOAD LINK HERE!
 
 # zkApps Umstad Chatbot
+
 ## Features
 
 The Umstad AI Assistant offers a comprehensive range of features designed to support developers in various aspects of the MINA Protocol and o1js:
 
 1. **Documentation & General Knowledge:**
+
    - Access to extensive resources including the Mina Protocol documentation, o1js guides, and Auro Wallet information.
    - Integration with Mina Book and Mina Blog for up-to-date knowledge and insights.
    - Availability of various project documentations to enhance understanding and application.
 
 2. **Example Codebase:**
+
    - Embedded code examples within the documentation for practical reference.
 
 3. **Projects Repositories:**
+
    - Embedded codebase of zkApps projects.
 
 4. **Community Interaction and Issue Tracking:**
+
    - Embedded and processed integration with the Mina Protocol Discord channels, specifically zkapps-developers and zkapps-questions, for community support and engagement.
    - Direct access to o1js GitHub Issues, providing insights into current challenges, bugs, and solutions within the community.
 
 5. **Mainnet Blockchain Information Access:**
-    - Direct access to Blockchain Summary, Block  and Account Information utilizing Mina Explorer API.
-    - Current MINA Price utilizing Coingecko API(TODO: Fix Fetching issue for deployment)
-
+   - Direct access to Blockchain Summary, Block and Account Information utilizing Mina Explorer API.
+   - Current MINA Price utilizing Coingecko API(TODO: Fix Fetching issue for deployment)
 
 ## Best Practices for Asking Questions
+
 1. **Be Specific:** Clearly state your issue or the topic you need assistance with.
 
 2. **Querying issues, errors, problems:** Begin with "I have an issue ..." or "I have a problem ..." for asking about errors, issues, problems, discussion, strange questions etc in order to utilize Issue Tool which queries the Issue Vectors which includes Discord and Github data.
 
-3. **Do not extend the conversation:** Even though ```gpt-4-1106-preview``` can handle overlong contexts, it is strongly advised that not to extend the conversation too much(For now, ideal 3 questions). Just create new chat after 2-3 questions because every query creates crowded context.
+3. **Do not extend the conversation:** Even though `gpt-4-1106-preview` can handle overlong contexts, it is strongly advised that not to extend the conversation too much(For now, ideal 3 questions). Just create new chat after 2-3 questions because every query creates crowded context.
 
 ## Technical Details
+
 ### Architecture Overview
 
 ### Backend Technologies
+
 - **Language and Frameworks:** Built using Next.js functions, OpenAI node.js ensuring robust and scalable performance.
 - **Database:** Uses vercel KV db.
 - **APIs:** Uses Vercel Functions.
 
 ### Security and Compliance
+
 - **Data Privacy:** We store your conversations in the KV db. Do not share confidentials, private keys etc. We are not responsible for Vercel's security.
 - **OPENAI API KEY:** Application stores your OpenAI API KEY on the browser local storage. So, if you are using shared computer, please remove your key in the settings panel.
 
 # zkApps Umstad CLI AGENT
+
 TODO
+
 ## Features
+
 TODO
+
 ## Usage
+
 TODO
+
 ### Installation
+
 TODO
+
 ### Getting Started
+
 TODO
+
 ### Best Practices for Agent
+
 TODO
 
 ## Architecture
 
-
 # Support
-For any technical issues or further inquiries, please contact our support team at [berkingurcan@gmail.com](mailto:berkingurcan@gmail.com).
 
+For any technical issues or further inquiries, please contact our support team at [berkingurcan@gmail.com](mailto:berkingurcan@gmail.com).
 
 # Acknowledgements
+
 Special thanks to the Mina Protocol zkIgnite program for funding the project and all contributors to this project.
diff --git a/app/api/embeddings/route.ts b/app/api/embeddings/route.ts
index 574d25b..294fce7 100644
--- a/app/api/embeddings/route.ts
+++ b/app/api/embeddings/route.ts
@@ -1,12 +1,39 @@
 import { tools } from '@/lib/tools'
+import { kv } from '@vercel/kv'
+import { Ratelimit } from '@upstash/ratelimit'
 
 export async function POST(req: Request) {
   const json = await req.json()
-  const { tool, query } = json
+  const { tool, query, project } = json
   const toolObject = tools.find(t => t.name === tool)
+
+  const ip = req.headers.get('x-forwarded-for')
+  const ratelimit = new Ratelimit({
+    redis: kv,
+    limiter: Ratelimit.slidingWindow(500, '1d')
+  })
+
+  const { success, limit, reset, remaining } = await ratelimit.limit(
+    `ratelimit_${ip}`
+  )
+
+  if (!success) {
+    return new Response('You have reached your request limit for the day.', {
+      status: 429,
+      headers: {
+        'X-RateLimit-Limit': limit.toString(),
+        'X-RateLimit-Remaining': remaining.toString(),
+        'X-RateLimit-Reset': reset.toString()
+      }
+    })
+  }
+
   if (!toolObject) {
     return new Response('Tool not found', { status: 404 })
   }
-  const result: string = await toolObject.callable({ query })
+
+  const result: string = project
+    ? await toolObject.callable({ query, project })
+    : await toolObject.callable({ query })
   return new Response(result, { status: 200 })
 }
diff --git a/app/api/evalapi/route.ts b/app/api/evalapi/route.ts
new file mode 100644
index 0000000..1761731
--- /dev/null
+++ b/app/api/evalapi/route.ts
@@ -0,0 +1,75 @@
+import { kv } from '@vercel/kv'
+import { OpenAIStream, StreamingTextResponse } from 'ai'
+import { Ratelimit } from '@upstash/ratelimit'
+import OpenAI from 'openai'
+import { NextResponse } from 'next/server'
+
+import { SYSTEM_PROMPT } from '../chat/prompts'
+import { validateApiKey } from '@/lib/utils'
+import { runnables } from '@/lib/tools'
+
+export const runtime = 'edge'
+
+export async function POST(req: Request) {
+  const json = await req.json()
+  const { message, previewToken } = json
+
+  let configuration
+  let model
+
+  const ip = req.headers.get('x-forwarded-for')
+
+  if (validateApiKey(previewToken)) {
+    configuration = {
+      apiKey: previewToken
+    }
+
+    const ratelimit = new Ratelimit({
+      redis: kv,
+      limiter: Ratelimit.slidingWindow(1000, '1d')
+    })
+
+    const { success, limit, reset, remaining } = await ratelimit.limit(
+      `ratelimit_${ip}`
+    )
+
+    if (!success) {
+      return new Response('You have reached your request limit for the day.', {
+        status: 429,
+        headers: {
+          'X-RateLimit-Limit': limit.toString(),
+          'X-RateLimit-Remaining': remaining.toString(),
+          'X-RateLimit-Reset': reset.toString()
+        }
+      })
+    }
+
+    model = 'gpt-4-1106-preview'
+    const openai = new OpenAI(configuration)
+
+    const runner = openai.beta.chat.completions.runTools({
+      stream: true,
+      model,
+      temperature: 0.2,
+      messages: [
+        {
+          role: 'system',
+          content: SYSTEM_PROMPT
+        },
+        {
+          role: 'user',
+          content: message
+        }
+      ],
+      tools: runnables
+    })
+
+    const stream = OpenAIStream(runner)
+    return new StreamingTextResponse(stream)
+  } else {
+    return NextResponse.json(
+      { error: 'OPENAI API KEY NOT FOUND' },
+      { status: 500 }
+    )
+  }
+}
diff --git a/cli/zkappumstad/tools/api_query.py b/cli/zkappumstad/tools/api_query.py
new file mode 100644
index 0000000..ab232f0
--- /dev/null
+++ b/cli/zkappumstad/tools/api_query.py
@@ -0,0 +1,49 @@
+import requests
+
+
+def query_index(tool, query):
+    url = "https://zkappsumstad.com/api/embeddings"
+
+    payload = {"tool": tool, "query": query}
+
+    try:
+        response = requests.post(url, json=payload)
+        if response.status_code == 200:
+            try:
+                return response.text
+            except ValueError as e:
+                return {
+                    "error": f"JSON decoding failed: {str(e)}",
+                    "response": response.text,
+                }
+        else:
+            return {
+                "error": f"Request failed with status code {response.status_code}",
+                "response": response.text,
+            }
+    except requests.exceptions.RequestException as e:
+        return {"error": f"Request failed: {str(e)}"}
+
+
+def query_project(tool, query, project_name):
+    url = "https://zkappsumstad.com/api/embeddings"
+
+    payload = {"tool": tool, "query": query, "project": project_name}
+
+    try:
+        response = requests.post(url, json=payload)
+        if response.status_code == 200:
+            try:
+                return response.text
+            except ValueError as e:
+                return {
+                    "error": f"JSON decoding failed: {str(e)}",
+                    "response": response.text,
+                }
+        else:
+            return {
+                "error": f"Request failed with status code {response.status_code}",
+                "response": response.text,
+            }
+    except requests.exceptions.RequestException as e:
+        return {"error": f"Request failed: {str(e)}"}
diff --git a/cli/zkappumstad/tools/code_tool.py b/cli/zkappumstad/tools/code_tool.py
index ce79b5f..f1f9f2b 100644
--- a/cli/zkappumstad/tools/code_tool.py
+++ b/cli/zkappumstad/tools/code_tool.py
@@ -1,20 +1,7 @@
-import os
-import pinecone
+import requests
 
 from .tool import Tool
-
-from openai import OpenAI
-from dotenv import load_dotenv, find_dotenv
-
-load_dotenv(find_dotenv(".env.local"))
-
-pinecone_api_key = os.getenv("PINECONE_API_KEY") or "YOUR_API_KEY"
-pinecone_env = os.getenv("PINECONE_ENVIRONMENT") or "YOUR_ENV"
-vector_type = os.getenv("CODE_VECTOR_TYPE") or "VECTOR_TYPE"
-
-pinecone.init(api_key=pinecone_api_key, environment=pinecone_env)
-
-client = OpenAI()
+from .api_query import query_index
 
 function_description = {
     "name": "search_for_code_context",
@@ -34,57 +21,13 @@
 function_messages = "Fetching context about code snippets..."
 
 
-def get_text_embeddings(query, model_name="text-embedding-ada-002"):
-    """Create text embeddings for a given query."""
-    try:
-        embeddings = client.embeddings.create(input=query, model=model_name)
-        return embeddings.data[0].embedding
-    except Exception as e:
-        print(f"Error in generating embeddings: {e}")
-        return None
-
-
-def query_index(embedding, index_name, top_k=5, vector_type=vector_type):
-    """Query the index with the given embedding."""
-    try:
-        index = pinecone.Index(index_name)
-        query_results = index.query(
-            vector=embedding,
-            top_k=top_k,
-            filter={"vector_type": vector_type},
-            include_metadata=True,
-        )
-        return query_results.to_dict()["matches"]
-    except Exception as e:
-        print(f"Error in querying index: {e}")
-        return []
-
-
-def format_results(matches):
-    """Format the query results for display."""
-    filtered_matches = [match for match in matches if match["score"] > 0.25]
-    formatted_texts = []
-    for i, match in enumerate(filtered_matches):
-        metadata = match["metadata"]
-        title = metadata.get("title", "")
-        text = metadata.get("text", "")
-        formatted_text = f"## Result {i + 1}:\n{title}\n{text}"
-        formatted_texts.append(formatted_text)
-    return "\n".join(formatted_texts)
-
-
-def run_tool(query="", vector_type=vector_type):
+def run_tool(query=""):
     """Run the query tool with the given parameters."""
-    embedding = get_text_embeddings(query)
-    if embedding is None:
-        return "Failed to generate embeddings."
-
-    matches = query_index(embedding, "zkappumstad", vector_type=vector_type)
+    matches = query_index(tool="search_for_code_context", query=query)
     if not matches:
         return "No matches found in the database for the query: " + query
 
-    formatted_text = format_results(matches)
-    return formatted_text
+    return matches
 
 
 code_tool = Tool(
diff --git a/cli/zkappumstad/tools/doc_tool.py b/cli/zkappumstad/tools/doc_tool.py
index 92e70b5..299ffac 100644
--- a/cli/zkappumstad/tools/doc_tool.py
+++ b/cli/zkappumstad/tools/doc_tool.py
@@ -1,20 +1,7 @@
-import os
-import pinecone
+import requests
 
 from .tool import Tool
-
-from openai import OpenAI
-from dotenv import load_dotenv, find_dotenv
-
-load_dotenv(find_dotenv(".env.local"))
-
-pinecone_api_key = os.getenv("PINECONE_API_KEY") or "YOUR_API_KEY"
-pinecone_env = os.getenv("PINECONE_ENVIRONMENT") or "YOUR_ENV"
-vector_type = os.getenv("DOCS_VECTOR_TYPE") or "VECTOR_TYPE"
-
-pinecone.init(api_key=pinecone_api_key, environment=pinecone_env)
-
-client = OpenAI()
+from .api_query import query_index
 
 function_description = {
     "name": "search_for_context",
@@ -34,55 +21,13 @@
 function_messages = "Fetching context about mina docs..."
 
 
-def get_text_embbeddings(query, model="text-embedding-ada-002"):
-    """Generate embeddings for a given query."""
-    try:
-        embeddings = client.embeddings.create(input=query, model=model)
-        return embeddings.data[0].embedding
-    except Exception as e:
-        print(f"Error generating embeddings: {e}")
-        return None
-
-
-def query_index(embedding, index_name="zkappumstad", top_k=7, vector_type=vector_type):
-    """Query the index using the generated embeddings."""
-    try:
-        index = pinecone.Index(index_name)
-        return index.query(
-            vector=embedding,
-            top_k=top_k,
-            filter={"vector_type": vector_type},
-            include_metadata=True,
-        ).to_dict()["matches"]
-    except Exception as e:
-        print(f"Error querying index: {e}")
-        return []
-
-
-def format_results(matches):
-    """Format the results from the index query."""
-    results = []
-    for i, match in enumerate(matches):
-        if match["score"] > 0.85:
-            metadata = match["metadata"]
-            title = metadata.get("title", "")
-            text = metadata.get("text", "")
-            formatted_result = f"## Result {i + 1}:\n{title}\n{text}"
-            results.append(formatted_result)
-    return "\n".join(results)
-
-
-def run_tool(query="", vector_type=vector_type):
+def run_tool(query=""):
     """Run the search tool with the provided query."""
-    embedding = get_text_embbeddings(query)
-    if embedding is None:
-        return "Failed to generate embeddings."
-
-    matches = query_index(embedding, vector_type=vector_type)
+    matches = query_index(tool="search_for_context", query=query)
     if not matches:
         return "No matches found."
 
-    return format_results(matches)
+    return matches
 
 
 doc_tool = Tool(
diff --git a/cli/zkappumstad/tools/issue_tool.py b/cli/zkappumstad/tools/issue_tool.py
index 0f967b9..bf95c71 100644
--- a/cli/zkappumstad/tools/issue_tool.py
+++ b/cli/zkappumstad/tools/issue_tool.py
@@ -1,20 +1,7 @@
-import os
-import pinecone
+import requests
 
 from .tool import Tool
-
-from openai import OpenAI
-from dotenv import load_dotenv, find_dotenv
-
-load_dotenv(find_dotenv(".env.local"))
-
-pinecone_api_key = os.getenv("PINECONE_API_KEY") or "YOUR_API_KEY"
-pinecone_env = os.getenv("PINECONE_ENVIRONMENT") or "YOUR_ENV"
-vector_type = os.getenv("ISSUE_VECTOR_TYPE") or "VECTOR_TYPE"
-
-pinecone.init(api_key=pinecone_api_key, environment=pinecone_env)
-
-client = OpenAI()
+from .api_query import query_index
 
 function_description = {
     "name": "search_for_issue_context",
@@ -34,55 +21,13 @@
 function_messages = "Fetching context about issues..."
 
 
-def get_text_embbeddings(query, model="text-embedding-ada-002"):
-    """Generate embeddings for a given query."""
-    try:
-        embeddings = client.embeddings.create(input=query, model=model)
-        return embeddings.data[0].embedding
-    except Exception as e:
-        print(f"Error generating embeddings: {e}")
-        return None
-
-
-def query_index(embedding, index_name="zkappumstad", top_k=3, vector_type=vector_type):
-    """Query the index using the generated embeddings."""
-    try:
-        index = pinecone.Index(index_name)
-        return index.query(
-            vector=embedding,
-            top_k=top_k,
-            filter={"vector_type": vector_type},
-            include_metadata=True,
-        ).to_dict()["matches"]
-    except Exception as e:
-        print(f"Error querying index: {e}")
-        return []
-
-
-def format_results(matches):
-    """Format the results from the index query."""
-    results = []
-    for i, match in enumerate(matches):
-        if match["score"] > 0.25:
-            metadata = match["metadata"]
-            title = metadata.get("title", "")
-            text = metadata.get("text", "")
-            formatted_result = f"## Result {i + 1}:\n{title}\n{text}"
-            results.append(formatted_result)
-    return "\n".join(results)
-
-
-def run_tool(query="", vector_type=vector_type):
+def run_tool(query=""):
     """Run the search tool with the provided query."""
-    embedding = get_text_embbeddings(query)
-    if embedding is None:
-        return "Failed to generate embeddings."
-
-    matches = query_index(embedding, vector_type=vector_type)
+    matches = query_index(tool="search_for_issue_context", query=query)
     if not matches:
         return "No matches found."
 
-    return format_results(matches)
+    return matches
 
 
 issue_tool = Tool(
diff --git a/cli/zkappumstad/tools/project_tool.py b/cli/zkappumstad/tools/project_tool.py
index acf0170..e0f1b5f 100644
--- a/cli/zkappumstad/tools/project_tool.py
+++ b/cli/zkappumstad/tools/project_tool.py
@@ -1,20 +1,8 @@
-import os
-import pinecone
+import requests
 
 from .tool import Tool
+from .api_query import query_project
 
-from openai import OpenAI
-from dotenv import load_dotenv, find_dotenv
-
-load_dotenv(find_dotenv(".env.local"))
-
-pinecone_api_key = os.getenv("PINECONE_API_KEY") or "YOUR_API_KEY"
-pinecone_env = os.getenv("PINECONE_ENVIRONMENT") or "YOUR_ENV"
-vector_type = os.getenv("PROJECT_VECTOR_TYPE") or "VECTOR_TYPE"
-
-pinecone.init(api_key=pinecone_api_key, environment=pinecone_env)
-
-client = OpenAI()
 
 function_description = {
     "name": "search_for_project_context",
@@ -38,72 +26,15 @@
 function_messages = "Fetching context about Projects..."
 
 
-def get_text_embbeddings(query, model="text-embedding-ada-002"):
-    """Generate embeddings for a given query."""
-    try:
-        embeddings = client.embeddings.create(input=query, model=model)
-        return embeddings.data[0].embedding
-    except Exception as e:
-        print(f"Error generating embeddings: {e}")
-        return None
-
-
-def query_index(
-    embedding,
-    index_name="zkappumstad",
-    top_k=3,
-    vector_type=vector_type,
-    project_name=None,
-):
-    """Query the index using the generated embeddings."""
-    try:
-        index = pinecone.Index(index_name)
-        if project_name:
-            filter_query = {
-                "vector_type": vector_type,
-                "project_name": {
-                    "$in": [f"Project Name: {project_name}", project_name]
-                },
-            }
-        else:
-            filter_query = {"vector_type": vector_type}
-
-        return index.query(
-            vector=embedding,
-            top_k=top_k,
-            filter=filter_query,
-            include_metadata=True,
-        ).to_dict()["matches"]
-
-    except Exception as e:
-        print(f"Error querying index: {e}")
-        return []
-
-
-def format_results(matches):
-    """Format the results from the index query."""
-    results = []
-    for i, match in enumerate(matches):
-        if match["score"] > 0.65:
-            metadata = match["metadata"]
-            title = metadata.get("title", "")
-            text = metadata.get("text", "")
-            formatted_result = f"## Result {i + 1}:\n{title}\n{text}"
-            results.append(formatted_result)
-    return "\n".join(results)
-
-
-def run_tool(query="", vector_type=vector_type, project_name=None):
+def run_tool(query="", project_name=None):
     """Run the search tool with the provided query."""
-    embedding = get_text_embbeddings(query)
-    if embedding is None:
-        return "Failed to generate embeddings."
-
-    matches = query_index(embedding, vector_type=vector_type, project_name=project_name)
+    matches = query_project(
+        tool="search_for_project_context", query=query, project_name=project_name
+    )
     if not matches:
         return "No matches found."
 
-    return format_results(matches)
+    return matches
 
 
 project_tool = Tool(
diff --git a/cli/zkappumstad/tools/read_reference_tool.py b/cli/zkappumstad/tools/read_reference_tool.py
index 183c8d1..bdc7702 100644
--- a/cli/zkappumstad/tools/read_reference_tool.py
+++ b/cli/zkappumstad/tools/read_reference_tool.py
@@ -26,11 +26,7 @@ def run_tool(directory=basedir):
         if not os.path.exists(directory):
             return f"The directory '{directory}' does not exist."
 
-        ts_files = [
-            f
-            for f in os.listdir(directory)
-            if f.endswith(".ts")
-        ]
+        ts_files = [f for f in os.listdir(directory) if f.endswith(".ts")]
 
         code_content = ""
         for filename in ts_files:
@@ -43,7 +39,6 @@ def run_tool(directory=basedir):
                      + Style.RESET_ALL
                 ) """
 
-
         return code_content
 
     except Exception as e:
diff --git a/lib/tools/code.ts b/lib/tools/code.ts
index 8b009e6..974bd64 100644
--- a/lib/tools/code.ts
+++ b/lib/tools/code.ts
@@ -36,7 +36,7 @@ async function formatResults(matches: ScoredPineconeRecord[]) {
   const results = []
   for (let i = 0; i < matches.length; i++) {
     const match = matches[i]
-    if ((match.score || 1) > 0.85) {
+    if ((match.score || 1) > 0.65) {
       const metadata = match.metadata as Metadata
       const title = metadata.text
       const text = metadata.text
diff --git a/lib/tools/issue.ts b/lib/tools/issue.ts
index a59c4c8..2725505 100644
--- a/lib/tools/issue.ts
+++ b/lib/tools/issue.ts
@@ -36,7 +36,7 @@ async function formatResults(matches: ScoredPineconeRecord[]) {
   const results = []
   for (let i = 0; i < matches.length; i++) {
     const match = matches[i]
-    if ((match.score || 1) > 0.85) {
+    if ((match.score || 1) > 0.75) {
       const metadata = match.metadata as Metadata
       const title = metadata.text
       const text = metadata.text
diff --git a/lib/tools/project.ts b/lib/tools/project.ts
index a405d9d..fb98ce2 100644
--- a/lib/tools/project.ts
+++ b/lib/tools/project.ts
@@ -41,7 +41,7 @@ async function formatResults(matches: ScoredPineconeRecord[]) {
   const results = []
   for (let i = 0; i < matches.length; i++) {
     const match = matches[i]
-    if ((match.score || 1) > 0.85) {
+    if ((match.score || 1) > 0.75) {
       const metadata = match.metadata as Metadata
       const title = metadata.text
       const text = metadata.text
@@ -52,9 +52,17 @@ async function formatResults(matches: ScoredPineconeRecord[]) {
   return results.join('\n')
 }
 
-async function runTool(args: { query: string }): Promise<string> {
+async function runTool(args: {
+  query: string
+  project_name?: string
+}): Promise<string> {
   const embeddings = await getEmbeddings(args.query)
-  const matches = await getMatchesFromEmbeddings(embeddings, 3, VECTOR_TYPE)
+  const matches = await getMatchesFromEmbeddings(
+    embeddings,
+    3,
+    VECTOR_TYPE,
+    args.project_name
+  )
 
   return formatResults(matches)
 }
diff --git a/lib/tools/utils/explorerScraper.ts b/lib/tools/utils/explorerScraper.ts
index 9074ac8..dd188b8 100644
--- a/lib/tools/utils/explorerScraper.ts
+++ b/lib/tools/utils/explorerScraper.ts
@@ -1,4 +1,4 @@
-'use server';
+'use server'
 
 const options = {
   method: 'GET',
@@ -81,10 +81,10 @@ export async function getCurrentPrice() {
     const response = await fetch(url, {
       method: 'GET',
       headers: {
-          'Content-Type': 'application/json',
+        'Content-Type': 'application/json'
       },
-      mode: 'cors',
-  })
+      mode: 'cors'
+    })
 
     if (!response.ok) {
       return 'Cannot fetch the API, response is not OK'
diff --git a/lib/tools/utils/pinecone.ts b/lib/tools/utils/pinecone.ts
index 7045470..f9c53d4 100644
--- a/lib/tools/utils/pinecone.ts
+++ b/lib/tools/utils/pinecone.ts
@@ -33,7 +33,8 @@ function isVectorType(type: string): type is VectorType {
 const getMatchesFromEmbeddings = async (
   embeddings: number[],
   topK: number,
-  vector_type: string
+  vector_type: string,
+  project_name?: string
 ): Promise<ScoredPineconeRecord<Metadata>[]> => {
   const pinecone = new Pinecone({
     environment: process.env.PINECONE_ENVIRONMENT as string,
@@ -54,19 +55,39 @@ const getMatchesFromEmbeddings = async (
 
   const index = pinecone!.Index<Metadata>(indexName)
 
-  try {
-    const queryResult = await index.query({
-      vector: embeddings,
-      topK,
-      filter: {
-        vector_type: vectorType
-      },
-      includeMetadata: true
-    })
-    return queryResult.matches || []
-  } catch (e) {
-    console.log('Error querying embeddings: ', e)
-    throw new Error(`Error querying embeddings: ${e}`)
+  if (project_name) {
+    try {
+      const queryResult = await index.query({
+        vector: embeddings,
+        topK,
+        filter: {
+          vector_type: vectorType,
+          project_name: {
+            $in: [`Project Name: ${project_name}`, project_name]
+          }
+        },
+        includeMetadata: true
+      })
+      return queryResult.matches || []
+    } catch (e) {
+      console.log('Error querying embeddings: ', e)
+      throw new Error(`Error querying embeddings: ${e}`)
+    }
+  } else {
+    try {
+      const queryResult = await index.query({
+        vector: embeddings,
+        topK,
+        filter: {
+          vector_type: vectorType
+        },
+        includeMetadata: true
+      })
+      return queryResult.matches || []
+    } catch (e) {
+      console.log('Error querying embeddings: ', e)
+      throw new Error(`Error querying embeddings: ${e}`)
+    }
   }
 }
 
diff --git a/next.config.js b/next.config.js
index bc2bcd6..9094503 100644
--- a/next.config.js
+++ b/next.config.js
@@ -4,12 +4,16 @@ module.exports = {
     return [
       {
         // matching all API routes
-        source: "/api/:path*",
+        source: '/api/:path*',
         headers: [
-          { key: "Access-Control-Allow-Credentials", value: "true" },
-          { key: "Access-Control-Allow-Origin", value: "*" },
-          { key: "Access-Control-Allow-Methods", value: "GET,OPTIONS" },
-          { key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" },
+          { key: 'Access-Control-Allow-Credentials', value: 'true' },
+          { key: 'Access-Control-Allow-Origin', value: '*' },
+          { key: 'Access-Control-Allow-Methods', value: 'GET,OPTIONS' },
+          {
+            key: 'Access-Control-Allow-Headers',
+            value:
+              'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
+          }
         ]
       }
     ]


Test patch:
diff --git a/cli/tool_test.py b/cli/tool_test.py
new file mode 100644
index 0000000..68489b0
--- /dev/null
+++ b/cli/tool_test.py
@@ -0,0 +1,4 @@
+from zkappumstad.tools.api_query import query_index
+
+x = query_index("search_for_code_context", "hello world")
+print(x)