yourmemory 1.4.45__tar.gz → 1.4.46__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {yourmemory-1.4.45 → yourmemory-1.4.46}/PKG-INFO +2 -1
- {yourmemory-1.4.45 → yourmemory-1.4.46}/pyproject.toml +2 -1
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/app.py +2 -1
- yourmemory-1.4.46/src/routes/proxy.py +294 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/yourmemory.egg-info/PKG-INFO +2 -1
- {yourmemory-1.4.45 → yourmemory-1.4.46}/yourmemory.egg-info/SOURCES.txt +1 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/yourmemory.egg-info/requires.txt +1 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/LICENSE +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/README.md +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/memory_mcp.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/setup.cfg +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/__init__.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/db/connection.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/db/duckdb_schema.sql +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/db/migrate.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/db/schema.sql +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/db/sqlite_schema.sql +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/graph/__init__.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/graph/backend.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/graph/graph_store.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/graph/neo4j_backend.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/graph/networkx_backend.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/graph/svo_extract.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/jobs/decay_job.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/routes/__init__.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/routes/agents.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/routes/graph_viz.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/routes/memories.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/routes/retrieve.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/routes/ui.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/__init__.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/agent_registry.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/api_keys.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/decay.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/embed.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/extract.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/extract_fallback.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/resolve.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/resolve_fallback.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/retrieve.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/session.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/temporal.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/src/services/utils.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/tests/test_features.py +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/yourmemory.egg-info/dependency_links.txt +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/yourmemory.egg-info/entry_points.txt +0 -0
- {yourmemory-1.4.45 → yourmemory-1.4.46}/yourmemory.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yourmemory
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.46
|
|
4
4
|
Summary: Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native
|
|
5
5
|
Author: Sachit Misra
|
|
6
6
|
Author-email: mishrasachit1@gmail.com
|
|
@@ -28,6 +28,7 @@ Requires-Dist: spacy<4.0,>=3.8.13; python_version < "3.14"
|
|
|
28
28
|
Requires-Dist: networkx>=3.0
|
|
29
29
|
Requires-Dist: fastapi
|
|
30
30
|
Requires-Dist: uvicorn[standard]
|
|
31
|
+
Requires-Dist: httpx
|
|
31
32
|
Provides-Extra: postgres
|
|
32
33
|
Requires-Dist: psycopg2-binary; extra == "postgres"
|
|
33
34
|
Requires-Dist: pgvector; extra == "postgres"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "yourmemory"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.46"
|
|
8
8
|
description = "Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -33,6 +33,7 @@ dependencies = [
|
|
|
33
33
|
"networkx>=3.0",
|
|
34
34
|
"fastapi",
|
|
35
35
|
"uvicorn[standard]",
|
|
36
|
+
"httpx",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[project.optional-dependencies]
|
|
@@ -7,7 +7,7 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
|
7
7
|
from fastapi import FastAPI
|
|
8
8
|
from fastapi.responses import StreamingResponse
|
|
9
9
|
from pydantic import BaseModel
|
|
10
|
-
from src.routes import memories, retrieve, agents, ui, graph_viz
|
|
10
|
+
from src.routes import memories, retrieve, agents, ui, graph_viz, proxy
|
|
11
11
|
from src.jobs.decay_job import run as run_decay
|
|
12
12
|
from src.db.migrate import migrate
|
|
13
13
|
|
|
@@ -31,6 +31,7 @@ app.include_router(retrieve.router)
|
|
|
31
31
|
app.include_router(agents.router)
|
|
32
32
|
app.include_router(ui.router)
|
|
33
33
|
app.include_router(graph_viz.router)
|
|
34
|
+
app.include_router(proxy.router)
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
@app.get("/health")
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import getpass
|
|
4
|
+
from fastapi import APIRouter, Request, HTTPException
|
|
5
|
+
from fastapi.responses import StreamingResponse, JSONResponse
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from src.services.retrieve import retrieve as _retrieve
|
|
9
|
+
|
|
10
|
+
router = APIRouter(prefix="/proxy")
|
|
11
|
+
|
|
12
|
+
OPENAI_BASE = "https://api.openai.com"
|
|
13
|
+
ANTHROPIC_BASE = "https://api.anthropic.com"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _user_id(request: Request) -> str:
|
|
17
|
+
return (
|
|
18
|
+
request.headers.get("x-yourmemory-user")
|
|
19
|
+
or os.getenv("YOURMEMORY_USER", "")
|
|
20
|
+
or getpass.getuser()
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _memory_block(user_id: str, query: str) -> str:
|
|
25
|
+
try:
|
|
26
|
+
result = _retrieve(user_id, query, top_k=5)
|
|
27
|
+
mems = result.get("memories", [])
|
|
28
|
+
if not mems:
|
|
29
|
+
return ""
|
|
30
|
+
lines = ["[Recalled from YourMemory]"]
|
|
31
|
+
for m in mems:
|
|
32
|
+
lines.append(f"- {m['content']}")
|
|
33
|
+
return "\n".join(lines)
|
|
34
|
+
except Exception:
|
|
35
|
+
return ""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _last_user_text(messages: list) -> str:
|
|
39
|
+
for m in reversed(messages):
|
|
40
|
+
if m.get("role") == "user":
|
|
41
|
+
c = m.get("content", "")
|
|
42
|
+
if isinstance(c, list):
|
|
43
|
+
return " ".join(p.get("text", "") for p in c if isinstance(p, dict))
|
|
44
|
+
return str(c)
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _execute_tool(name: str, args: dict, user_id: str) -> str:
|
|
49
|
+
try:
|
|
50
|
+
if name == "store_memory":
|
|
51
|
+
from src.services.retrieve import retrieve as _r
|
|
52
|
+
from src.db.connection import get_connection
|
|
53
|
+
from src.services.embed import embed
|
|
54
|
+
import numpy as np
|
|
55
|
+
content = args.get("content", "")
|
|
56
|
+
importance = float(args.get("importance", 0.7))
|
|
57
|
+
category = args.get("category", "fact")
|
|
58
|
+
if not content:
|
|
59
|
+
return "error: content required"
|
|
60
|
+
# Use the existing store logic via HTTP to local server to avoid circular imports
|
|
61
|
+
import urllib.request as _ur
|
|
62
|
+
payload = json.dumps({
|
|
63
|
+
"userId": user_id,
|
|
64
|
+
"content": content,
|
|
65
|
+
"importance": importance,
|
|
66
|
+
"category": category,
|
|
67
|
+
}).encode()
|
|
68
|
+
req = _ur.Request("http://localhost:3033/memories", data=payload,
|
|
69
|
+
headers={"Content-Type": "application/json"}, method="POST")
|
|
70
|
+
with _ur.urlopen(req, timeout=5) as resp:
|
|
71
|
+
return resp.read().decode()
|
|
72
|
+
|
|
73
|
+
if name == "update_memory":
|
|
74
|
+
memory_id = args.get("memory_id") or args.get("id")
|
|
75
|
+
new_content = args.get("new_content") or args.get("content", "")
|
|
76
|
+
importance = float(args.get("importance", 0.7))
|
|
77
|
+
if not memory_id or not new_content:
|
|
78
|
+
return "error: memory_id and new_content required"
|
|
79
|
+
import urllib.request as _ur
|
|
80
|
+
payload = json.dumps({
|
|
81
|
+
"userId": user_id,
|
|
82
|
+
"newContent": new_content,
|
|
83
|
+
"importance": importance,
|
|
84
|
+
}).encode()
|
|
85
|
+
req = _ur.Request(f"http://localhost:3033/memories/{memory_id}", data=payload,
|
|
86
|
+
headers={"Content-Type": "application/json"}, method="PUT")
|
|
87
|
+
with _ur.urlopen(req, timeout=5) as resp:
|
|
88
|
+
return resp.read().decode()
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
return f"error: {e}"
|
|
92
|
+
return "ok"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ── Tool schemas injected into every request ───────────────────────────────────
|
|
96
|
+
|
|
97
|
+
OPENAI_TOOLS = [
|
|
98
|
+
{
|
|
99
|
+
"type": "function",
|
|
100
|
+
"function": {
|
|
101
|
+
"name": "store_memory",
|
|
102
|
+
"description": "Store a new fact about the user or project for future sessions.",
|
|
103
|
+
"parameters": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"properties": {
|
|
106
|
+
"content": {"type": "string", "description": "One sentence fact to remember."},
|
|
107
|
+
"importance": {"type": "number", "description": "0.0–1.0. Use 0.9 for core identity, 0.7 for preferences, 0.5 for regular facts."},
|
|
108
|
+
"category": {"type": "string", "enum": ["fact", "strategy", "assumption", "failure"]},
|
|
109
|
+
},
|
|
110
|
+
"required": ["content", "importance"],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"type": "function",
|
|
116
|
+
"function": {
|
|
117
|
+
"name": "update_memory",
|
|
118
|
+
"description": "Update or correct an existing memory by ID.",
|
|
119
|
+
"parameters": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"properties": {
|
|
122
|
+
"memory_id": {"type": "string", "description": "ID of the memory to update."},
|
|
123
|
+
"new_content": {"type": "string", "description": "Replacement content."},
|
|
124
|
+
"importance": {"type": "number"},
|
|
125
|
+
},
|
|
126
|
+
"required": ["memory_id", "new_content", "importance"],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
ANTHROPIC_TOOLS = [
|
|
133
|
+
{
|
|
134
|
+
"name": "store_memory",
|
|
135
|
+
"description": "Store a new fact about the user or project for future sessions.",
|
|
136
|
+
"input_schema": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"properties": {
|
|
139
|
+
"content": {"type": "string"},
|
|
140
|
+
"importance": {"type": "number"},
|
|
141
|
+
"category": {"type": "string", "enum": ["fact", "strategy", "assumption", "failure"]},
|
|
142
|
+
},
|
|
143
|
+
"required": ["content", "importance"],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"name": "update_memory",
|
|
148
|
+
"description": "Update or correct an existing memory by ID.",
|
|
149
|
+
"input_schema": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"properties": {
|
|
152
|
+
"memory_id": {"type": "string"},
|
|
153
|
+
"new_content": {"type": "string"},
|
|
154
|
+
"importance": {"type": "number"},
|
|
155
|
+
},
|
|
156
|
+
"required": ["memory_id", "new_content", "importance"],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# ── OpenAI-compatible proxy ────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
@router.post("/openai/v1/chat/completions")
|
|
165
|
+
async def proxy_openai(request: Request):
|
|
166
|
+
auth = request.headers.get("authorization", "")
|
|
167
|
+
if not auth.startswith("Bearer "):
|
|
168
|
+
raise HTTPException(status_code=401, detail="Missing OpenAI API key in Authorization header")
|
|
169
|
+
|
|
170
|
+
body = await request.json()
|
|
171
|
+
uid = _user_id(request)
|
|
172
|
+
messages = body.get("messages", [])
|
|
173
|
+
block = _memory_block(uid, _last_user_text(messages))
|
|
174
|
+
|
|
175
|
+
if block:
|
|
176
|
+
if messages and messages[0].get("role") == "system":
|
|
177
|
+
messages[0]["content"] = block + "\n\n" + messages[0]["content"]
|
|
178
|
+
else:
|
|
179
|
+
messages.insert(0, {"role": "system", "content": block})
|
|
180
|
+
body["messages"] = messages
|
|
181
|
+
|
|
182
|
+
# Merge memory tools with any existing tools
|
|
183
|
+
existing_tools = body.get("tools", [])
|
|
184
|
+
body["tools"] = existing_tools + OPENAI_TOOLS
|
|
185
|
+
if body.get("tool_choice") is None:
|
|
186
|
+
body["tool_choice"] = "auto"
|
|
187
|
+
|
|
188
|
+
headers = {"Authorization": auth, "Content-Type": "application/json"}
|
|
189
|
+
stream = body.get("stream", False)
|
|
190
|
+
|
|
191
|
+
async with httpx.AsyncClient(timeout=120) as client:
|
|
192
|
+
if stream:
|
|
193
|
+
# For streaming: pass through — tool calls still appear in stream chunks
|
|
194
|
+
# but we can't intercept mid-stream easily. Model stores via next turn.
|
|
195
|
+
async def _stream():
|
|
196
|
+
async with client.stream("POST", f"{OPENAI_BASE}/v1/chat/completions",
|
|
197
|
+
headers=headers, json=body) as r:
|
|
198
|
+
async for chunk in r.aiter_bytes():
|
|
199
|
+
yield chunk
|
|
200
|
+
return StreamingResponse(_stream(), media_type="text/event-stream")
|
|
201
|
+
|
|
202
|
+
# Non-streaming: intercept tool calls and execute them
|
|
203
|
+
r = await client.post(f"{OPENAI_BASE}/v1/chat/completions", headers=headers, json=body)
|
|
204
|
+
data = r.json()
|
|
205
|
+
|
|
206
|
+
choice = (data.get("choices") or [{}])[0]
|
|
207
|
+
msg = choice.get("message", {})
|
|
208
|
+
tool_calls = msg.get("tool_calls", [])
|
|
209
|
+
|
|
210
|
+
if tool_calls:
|
|
211
|
+
# Execute each memory tool call
|
|
212
|
+
messages.append(msg)
|
|
213
|
+
for tc in tool_calls:
|
|
214
|
+
fn = tc.get("function", {})
|
|
215
|
+
name = fn.get("name", "")
|
|
216
|
+
args = json.loads(fn.get("arguments", "{}"))
|
|
217
|
+
if name in ("store_memory", "update_memory"):
|
|
218
|
+
result = _execute_tool(name, args, uid)
|
|
219
|
+
messages.append({
|
|
220
|
+
"role": "tool",
|
|
221
|
+
"tool_call_id": tc["id"],
|
|
222
|
+
"content": result,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
# Second call to get the final response
|
|
226
|
+
body["messages"] = messages
|
|
227
|
+
body.pop("tools", None)
|
|
228
|
+
body.pop("tool_choice", None)
|
|
229
|
+
r2 = await client.post(f"{OPENAI_BASE}/v1/chat/completions", headers=headers, json=body)
|
|
230
|
+
return JSONResponse(content=r2.json(), status_code=r2.status_code)
|
|
231
|
+
|
|
232
|
+
return JSONResponse(content=data, status_code=r.status_code)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ── Anthropic-compatible proxy ─────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
@router.post("/anthropic/v1/messages")
|
|
238
|
+
async def proxy_anthropic(request: Request):
|
|
239
|
+
api_key = request.headers.get("x-api-key", "")
|
|
240
|
+
if not api_key:
|
|
241
|
+
raise HTTPException(status_code=401, detail="Missing Anthropic API key in x-api-key header")
|
|
242
|
+
|
|
243
|
+
body = await request.json()
|
|
244
|
+
uid = _user_id(request)
|
|
245
|
+
messages = body.get("messages", [])
|
|
246
|
+
block = _memory_block(uid, _last_user_text(messages))
|
|
247
|
+
|
|
248
|
+
if block:
|
|
249
|
+
existing = body.get("system", "")
|
|
250
|
+
body["system"] = block + ("\n\n" + existing if existing else "")
|
|
251
|
+
|
|
252
|
+
body["tools"] = body.get("tools", []) + ANTHROPIC_TOOLS
|
|
253
|
+
|
|
254
|
+
headers = {
|
|
255
|
+
"x-api-key": api_key,
|
|
256
|
+
"anthropic-version": request.headers.get("anthropic-version", "2023-06-01"),
|
|
257
|
+
"content-type": "application/json",
|
|
258
|
+
}
|
|
259
|
+
stream = body.get("stream", False)
|
|
260
|
+
|
|
261
|
+
async with httpx.AsyncClient(timeout=120) as client:
|
|
262
|
+
if stream:
|
|
263
|
+
async def _stream():
|
|
264
|
+
async with client.stream("POST", f"{ANTHROPIC_BASE}/v1/messages",
|
|
265
|
+
headers=headers, json=body) as r:
|
|
266
|
+
async for chunk in r.aiter_bytes():
|
|
267
|
+
yield chunk
|
|
268
|
+
return StreamingResponse(_stream(), media_type="text/event-stream")
|
|
269
|
+
|
|
270
|
+
r = await client.post(f"{ANTHROPIC_BASE}/v1/messages", headers=headers, json=body)
|
|
271
|
+
data = r.json()
|
|
272
|
+
|
|
273
|
+
# Intercept tool use blocks
|
|
274
|
+
content_blocks = data.get("content", [])
|
|
275
|
+
tool_uses = [b for b in content_blocks if b.get("type") == "tool_use"
|
|
276
|
+
and b.get("name") in ("store_memory", "update_memory")]
|
|
277
|
+
|
|
278
|
+
if tool_uses:
|
|
279
|
+
messages.append({"role": "assistant", "content": content_blocks})
|
|
280
|
+
tool_results = []
|
|
281
|
+
for tu in tool_uses:
|
|
282
|
+
result = _execute_tool(tu["name"], tu.get("input", {}), uid)
|
|
283
|
+
tool_results.append({
|
|
284
|
+
"type": "tool_result",
|
|
285
|
+
"tool_use_id": tu["id"],
|
|
286
|
+
"content": result,
|
|
287
|
+
})
|
|
288
|
+
messages.append({"role": "user", "content": tool_results})
|
|
289
|
+
body["messages"] = messages
|
|
290
|
+
body.pop("tools", None)
|
|
291
|
+
r2 = await client.post(f"{ANTHROPIC_BASE}/v1/messages", headers=headers, json=body)
|
|
292
|
+
return JSONResponse(content=r2.json(), status_code=r2.status_code)
|
|
293
|
+
|
|
294
|
+
return JSONResponse(content=data, status_code=r.status_code)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yourmemory
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.46
|
|
4
4
|
Summary: Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native
|
|
5
5
|
Author: Sachit Misra
|
|
6
6
|
Author-email: mishrasachit1@gmail.com
|
|
@@ -28,6 +28,7 @@ Requires-Dist: spacy<4.0,>=3.8.13; python_version < "3.14"
|
|
|
28
28
|
Requires-Dist: networkx>=3.0
|
|
29
29
|
Requires-Dist: fastapi
|
|
30
30
|
Requires-Dist: uvicorn[standard]
|
|
31
|
+
Requires-Dist: httpx
|
|
31
32
|
Provides-Extra: postgres
|
|
32
33
|
Requires-Dist: psycopg2-binary; extra == "postgres"
|
|
33
34
|
Requires-Dist: pgvector; extra == "postgres"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|