-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcontext.py
More file actions
executable file
·152 lines (129 loc) · 5.99 KB
/
Copy pathcontext.py
File metadata and controls
executable file
·152 lines (129 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "pinecone==9.1.0",
# "typer>=0.15.0",
# "rich>=13.0.0",
# ]
# ///
"""
Retrieve context snippets from a Pinecone Assistant's knowledge base.
Usage:
uv run context.py --assistant NAME --query "search text" [--top-k 5] [--json]
Environment Variables:
PINECONE_API_KEY: Required Pinecone API key
Output:
Relevant context snippets with file sources, page numbers, and relevance scores
"""
import os
import json as json_module
import typer
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
from pinecone import Pinecone
from pinecone.models.assistant import TextSnippet
app = typer.Typer()
console = Console()
@app.command()
def main(
assistant: str = typer.Option(..., "--assistant", "-a", help="Name of the assistant"),
query: str = typer.Option(..., "--query", "-q", help="Search query text"),
top_k: int = typer.Option(5, "--top-k", "-k", help="Number of results to return (max 16)"),
snippet_size: int = typer.Option(1024, "--snippet-size", "-s", help="Maximum tokens per snippet"),
json: bool = typer.Option(False, "--json", help="Output in JSON format"),
):
"""Retrieve relevant context snippets from an assistant's knowledge base."""
# Check for API key
api_key = os.environ.get("PINECONE_API_KEY")
if not api_key:
console.print("[red]Error: PINECONE_API_KEY environment variable not set[/red]")
console.print("\nGet your API key from: https://app.pinecone.io/?sessionType=signup")
raise typer.Exit(1)
try:
# Initialize Pinecone client
pc = Pinecone(api_key=api_key, source_tag="cursor_plugin:assistant")
# Display query
if not json:
console.print(Panel(f"[bold cyan]Query:[/bold cyan] {query}", border_style="cyan"))
# Retrieve context
with console.status("[bold blue]Searching knowledge base...[/bold blue]", spinner="dots"):
response = pc.assistants.context(
assistant_name=assistant, query=query, top_k=top_k, snippet_size=snippet_size
)
# Get snippets from response
snippets = response.snippets if hasattr(response, 'snippets') else []
if json:
# JSON output
results = []
for snippet in snippets:
file_name = "Unknown"
pages = []
if hasattr(snippet, 'reference') and snippet.reference:
ref = snippet.reference
if hasattr(ref, 'file') and hasattr(ref.file, 'name'):
file_name = ref.file.name
if hasattr(ref, 'pages') and ref.pages:
pages = ref.pages
results.append({
"file_name": file_name,
"pages": pages,
"content": getattr(snippet, 'content', ''),
"score": getattr(snippet, 'score', 0.0),
# SDK 9 encodes the snippet kind as a msgspec tag, not an
# instance attribute, so getattr(snippet, 'type') silently
# returned the default for every snippet.
"type": "text" if isinstance(snippet, TextSnippet) else "multimodal",
})
print(json_module.dumps({"snippets": results, "count": len(results)}, indent=2))
else:
# Rich formatted output
if not snippets or len(snippets) == 0:
console.print("[yellow]No context found for this query[/yellow]")
return
console.print(f"\n[bold]Found {len(snippets)} relevant snippet(s):[/bold]\n")
for idx, snippet in enumerate(snippets, 1):
# Extract file info from reference
file_name = "Unknown"
pages = []
if hasattr(snippet, 'reference') and snippet.reference:
ref = snippet.reference
if hasattr(ref, 'file') and hasattr(ref.file, 'name'):
file_name = ref.file.name
if hasattr(ref, 'pages') and ref.pages:
pages = ref.pages
score = getattr(snippet, 'score', 0.0)
content = getattr(snippet, 'content', '')
# Create header
header = f"#{idx} - {file_name}"
if pages:
pages_str = ", ".join(str(p) for p in pages)
header += f" (Page {pages_str})"
header += f" - Score: {score:.3f}" if isinstance(score, (int, float)) else f" - Score: {score}"
console.print(Panel(
content,
title=header,
border_style="blue",
subtitle=f"[dim]Relevance: {score:.2%}[/dim]" if isinstance(score, (int, float)) else None
))
console.print()
# Suggest next action
next_action = f"""[bold]Next steps:[/bold]
• Ask a question: [cyan]/pinecone:assistant-chat assistant {assistant} message [your question][/cyan]
• Upload more files: [cyan]/pinecone:assistant-upload assistant {assistant} source [path][/cyan]"""
console.print(Panel(next_action, title="What's Next?", border_style="green"))
except AttributeError as e:
# Handle case where context method doesn't exist or response structure is different
console.print(f"[red]Error: Context retrieval failed[/red]")
console.print(f"[dim]Details: {e}[/dim]")
console.print("\n[yellow]Note:[/yellow] Context API requires SDK version with assistant.context() support")
console.print("\n[yellow]Try using chat instead:[/yellow]")
console.print(f" /pinecone:assistant-chat assistant {assistant} message \"{query}\"")
raise typer.Exit(1)
except Exception as e:
console.print(f"[red]Error: {e}[/red]")
raise typer.Exit(1)
if __name__ == "__main__":
app()