openai-sqlite-cache 0.0.1__py3-none-any.whl

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.
@@ -0,0 +1,122 @@
1
+ """Cache settings and platform-appropriate defaults."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from dataclasses import dataclass, replace
8
+ from pathlib import Path
9
+ from threading import RLock
10
+ from typing import Optional, Union
11
+
12
+ PathLike = Union[str, os.PathLike[str]]
13
+
14
+
15
+ def default_cache_path() -> Path:
16
+ """Return a user-local cache path without requiring another dependency."""
17
+
18
+ configured = os.environ.get("CACHED_OPENAI_CACHE_PATH")
19
+ if configured:
20
+ return Path(configured).expanduser()
21
+
22
+ if sys.platform == "darwin":
23
+ root = Path.home() / "Library" / "Caches"
24
+ elif os.name == "nt":
25
+ root = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
26
+ else:
27
+ root = Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache"))
28
+ return root / "openai-sqlite-cache" / "cache.sqlite3"
29
+
30
+
31
+ def _env_flag(name: str, default: bool) -> bool:
32
+ value = os.environ.get(name)
33
+ if value is None:
34
+ return default
35
+ return value.strip().lower() not in {"0", "false", "no", "off"}
36
+
37
+
38
+ def _env_ttl() -> Optional[float]:
39
+ value = os.environ.get("CACHED_OPENAI_TTL_SECONDS", "").strip()
40
+ if not value:
41
+ return None
42
+ ttl = float(value)
43
+ if ttl < 0:
44
+ raise ValueError("CACHED_OPENAI_TTL_SECONDS must be zero or greater")
45
+ return ttl
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class CacheSettings:
50
+ path: Path
51
+ ttl_seconds: Optional[float] = None
52
+ enabled: bool = True
53
+
54
+
55
+ _settings_lock = RLock()
56
+ _settings: Optional[CacheSettings] = None
57
+
58
+
59
+ def get_settings() -> CacheSettings:
60
+ global _settings
61
+ with _settings_lock:
62
+ if _settings is None:
63
+ _settings = CacheSettings(
64
+ path=default_cache_path(),
65
+ ttl_seconds=_env_ttl(),
66
+ enabled=not _env_flag("CACHED_OPENAI_DISABLE", False),
67
+ )
68
+ return _settings
69
+
70
+
71
+ _UNSET = object()
72
+
73
+
74
+ def configure_settings(
75
+ *,
76
+ path: Union[PathLike, object] = _UNSET,
77
+ ttl_seconds: Union[Optional[float], object] = _UNSET,
78
+ enabled: Union[bool, object] = _UNSET,
79
+ ) -> CacheSettings:
80
+ global _settings
81
+ with _settings_lock:
82
+ current = get_settings()
83
+ changes = {}
84
+ if path is not _UNSET:
85
+ changes["path"] = Path(path).expanduser() # type: ignore[arg-type]
86
+ if ttl_seconds is not _UNSET:
87
+ if ttl_seconds is not None and float(ttl_seconds) < 0: # type: ignore[arg-type]
88
+ raise ValueError("ttl_seconds must be zero or greater")
89
+ changes["ttl_seconds"] = (
90
+ None if ttl_seconds is None else float(ttl_seconds) # type: ignore[arg-type]
91
+ )
92
+ if enabled is not _UNSET:
93
+ changes["enabled"] = bool(enabled)
94
+ _settings = replace(current, **changes)
95
+ return _settings
96
+
97
+
98
+ def settings_for_client(
99
+ *,
100
+ cache_path: Union[Optional[PathLike], object] = _UNSET,
101
+ cache_ttl: Union[Optional[float], object] = _UNSET,
102
+ cache_enabled: Union[Optional[bool], object] = _UNSET,
103
+ ) -> CacheSettings:
104
+ current = get_settings()
105
+ path = (
106
+ current.path
107
+ if cache_path is _UNSET or cache_path is None
108
+ else Path(cache_path).expanduser()
109
+ )
110
+ ttl = current.ttl_seconds if cache_ttl is _UNSET else cache_ttl
111
+ enabled = (
112
+ current.enabled
113
+ if cache_enabled is _UNSET or cache_enabled is None
114
+ else cache_enabled
115
+ )
116
+ if ttl is not None and float(ttl) < 0:
117
+ raise ValueError("cache_ttl must be zero or greater")
118
+ return CacheSettings(
119
+ path=path,
120
+ ttl_seconds=None if ttl is None else float(ttl),
121
+ enabled=bool(enabled),
122
+ )
@@ -0,0 +1,3 @@
1
+ """Package version information."""
2
+
3
+ PACKAGE_VERSION = "0.0.1"
cached_openai/py.typed ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: openai_sqlite_cache
3
+ Version: 0.0.1
4
+ Summary: An unofficial drop-in OpenAI Python SDK with a local SQLite response cache
5
+ Author: openai_sqlite_cache contributors
6
+ License-Expression: MIT
7
+ Keywords: openai,cache,sqlite,llm,api
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: openai<3,>=1.66.2
22
+ Dynamic: license-file
23
+
24
+ # openai_sqlite_cache
25
+
26
+ `openai_sqlite_cache` 是非官方的 OpenAI Python SDK 透明 SQLite 缓存层。同一个 API、同一个模型、完全相同的输入再次调用时,会直接返回本机缓存的响应,不再请求上游。
27
+
28
+ PyPI 分发名是 `openai_sqlite_cache`(规范化名称为 `openai-sqlite-cache`);为满足只替换 import 的用法,Python 导入名仍然是 `cached_openai`。
29
+
30
+ ## 安装
31
+
32
+ 在当前仓库中安装:
33
+
34
+ ```bash
35
+ pip install ./openai_sqlite_cache
36
+ ```
37
+
38
+ 发布到你有权限的 Python 包索引后,安装形式为:
39
+
40
+ ```bash
41
+ pip install openai_sqlite_cache
42
+ ```
43
+
44
+ > 注意:本项目与公共 PyPI 上的 `cached-openai` 分发项目都提供 `cached_openai` 导入包,二者不能安全地安装在同一个 Python 环境中。请只安装其中一个。
45
+
46
+ ## 用法
47
+
48
+ 现有代码只需替换 import,并保留 `openai` 这个本地别名:
49
+
50
+ ```python
51
+ # 原来:import openai
52
+ import cached_openai as openai
53
+
54
+ client = openai.OpenAI()
55
+ response = client.responses.create(
56
+ model="gpt-5",
57
+ input="Explain SQLite in one sentence.",
58
+ )
59
+ print(response.output_text)
60
+ ```
61
+
62
+ 也可以直接使用包名:
63
+
64
+ ```python
65
+ import cached_openai
66
+
67
+ client = cached_openai.OpenAI()
68
+ completion = client.chat.completions.create(
69
+ model="gpt-4.1-mini",
70
+ messages=[{"role": "user", "content": "Hello"}],
71
+ )
72
+ ```
73
+
74
+ 以下官方 SDK 用法均保持不变:
75
+
76
+ - `OpenAI`、`AsyncOpenAI`、`AzureOpenAI` 和 `AsyncAzureOpenAI`;
77
+ - 模块级调用,如 `openai.chat.completions.create(...)`;
78
+ - `with_options()`、`with_raw_response`、`with_streaming_response`;
79
+ - 自定义 `base_url` 和自定义同步/异步 `http_client`;
80
+ - 官方 SDK 的响应模型和异常类型。
81
+
82
+ 流式响应会在第一次被完整消费时写入缓存;如果流被提前关闭,则不会缓存不完整内容。Realtime/WebSocket 不经过普通 HTTP 请求,因此不会缓存。
83
+
84
+ ## 缓存规则
85
+
86
+ 缓存键使用 SHA-256 计算,包含:
87
+
88
+ - HTTP 方法和规范化后的完整 API URL;
89
+ - 请求体(JSON 会按键排序,multipart 会忽略随机 boundary 并对文件内容取 hash);
90
+ - 模型名;
91
+ - organization、project、beta 等会影响语义的请求头;
92
+ - API 凭据的不可逆作用域 hash,防止不同 API key 之间共享响应。
93
+
94
+ 请求原文和 API key 都不会写入数据库。数据库只保存 hash、成功响应体、必要响应头和命中统计。默认永久保留,只缓存 2xx 响应。
95
+
96
+ 为避免有副作用的操作被错误去重,目前只缓存这些推理/生成端点:Responses、Chat Completions、Completions、Embeddings、Moderations、Images、Audio 和 Videos。Files、Uploads、Batches、Fine-tuning、Vector Stores 等管理类 API 会原样请求上游。
97
+
98
+ ## 配置
99
+
100
+ 默认数据库位置:
101
+
102
+ - macOS:`~/Library/Caches/openai-sqlite-cache/cache.sqlite3`
103
+ - Linux:`$XDG_CACHE_HOME/openai-sqlite-cache/cache.sqlite3`,未设置时使用 `~/.cache/...`
104
+ - Windows:`%LOCALAPPDATA%/openai-sqlite-cache/cache.sqlite3`
105
+
106
+ 环境变量:
107
+
108
+ ```bash
109
+ export CACHED_OPENAI_CACHE_PATH=/path/to/cache.sqlite3
110
+ export CACHED_OPENAI_TTL_SECONDS=86400
111
+ export CACHED_OPENAI_DISABLE=0
112
+ ```
113
+
114
+ 也可以在创建客户端前配置:
115
+
116
+ ```python
117
+ import cached_openai as openai
118
+
119
+ openai.configure_cache(
120
+ path="./.cache/openai.sqlite3",
121
+ ttl_seconds=24 * 60 * 60,
122
+ )
123
+ client = openai.OpenAI()
124
+ ```
125
+
126
+ 单个客户端可以覆盖全局配置:
127
+
128
+ ```python
129
+ client = openai.OpenAI(
130
+ cache_path="./project-cache.sqlite3",
131
+ cache_ttl=3600,
132
+ cache_enabled=True,
133
+ )
134
+ ```
135
+
136
+ 维护接口:
137
+
138
+ ```python
139
+ print(openai.cache_info())
140
+ removed = openai.clear_cache()
141
+ ```
142
+
143
+ 缓存文件可能包含模型响应中的敏感内容,应像其他本地应用数据一样保护。包会尽量把目录和数据库权限分别设为 `0700` 和 `0600`。
144
+
145
+ ## 开发与测试
146
+
147
+ ```bash
148
+ cd openai_sqlite_cache
149
+ python -m unittest discover -s tests -v
150
+ python -m pip wheel --no-deps . -w dist
151
+ ```
@@ -0,0 +1,14 @@
1
+ cached_openai/__init__.py,sha256=SrjO8Mntmt7OiPUaQlR9xCoQcWPX6xAUhrfYzlRWNs0,4703
2
+ cached_openai/_cache.py,sha256=TJizYukVUsBonsvTnqi7Bmx62B9sLi0ZbzC-hGUuTmo,4978
3
+ cached_openai/_client.py,sha256=oDZokr-B6TT1gfz-u0ANFDEC2zfL1QCTjFN4PYNDCX8,4618
4
+ cached_openai/_fingerprint.py,sha256=yhkt5ZixD7TO80-xD7MklmiIZuhUjeiuyPhfE-6GPv0,6234
5
+ cached_openai/_http.py,sha256=5_Ky-YPrD-s6kcg8x_sdvOSQYwdhKH6k-8md9mWY56U,17488
6
+ cached_openai/_module_alias.py,sha256=QYHlNKyds6eD-Mfic81-9aOKKEGebXefzmH0mnBsw08,2973
7
+ cached_openai/_settings.py,sha256=ZpOVq4ngCKjnmzTxbB-b48N0WLaEI4w4HfgXmhgwyDw,3657
8
+ cached_openai/_version.py,sha256=yXe71I5Ds7WgI-daxeCN0TpK2xgujsfxH7z7GjPCZqA,62
9
+ cached_openai/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
10
+ openai_sqlite_cache-0.0.1.dist-info/licenses/LICENSE,sha256=3nKN1iGgSEww3R8uNaFoxf54TmEQVaJr7Fw3draZZVI,1089
11
+ openai_sqlite_cache-0.0.1.dist-info/METADATA,sha256=zdvmq8DWv_nrn2grSd4AFWmZlCNh-DvUnvAy1NuBTz0,4847
12
+ openai_sqlite_cache-0.0.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
13
+ openai_sqlite_cache-0.0.1.dist-info/top_level.txt,sha256=DlzMut0JtFH9dwVNMedzyoKmV3m03J_dUFWq2LHYpDA,14
14
+ openai_sqlite_cache-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 openai_sqlite_cache contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ cached_openai