mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-14 07:52:25 -06:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3934a2d14 | |||
| 3d02cf66b4 | |||
| 7631b88792 | |||
| a07172b0c0 |
+2
-2
@@ -25,12 +25,12 @@ ENV UV_COMPILE_BYTECODE=1
|
||||
# Install dependencies first (cached layer — only re-runs when deps change)
|
||||
COPY pyproject.toml uv.lock README.md LICENSE ./
|
||||
RUN uv sync --frozen --no-install-project --no-dev \
|
||||
--extra mq --extra console --extra sim --extra postgres --extra discord --extra anthropic
|
||||
--extra mq --extra console --extra sim --extra postgres --extra discord --extra anthropic --extra ddg
|
||||
|
||||
# Install the project itself
|
||||
COPY turnstone/ turnstone/
|
||||
RUN uv sync --frozen --no-dev \
|
||||
--extra mq --extra console --extra sim --extra postgres --extra discord --extra anthropic
|
||||
--extra mq --extra console --extra sim --extra postgres --extra discord --extra anthropic --extra ddg
|
||||
|
||||
# Add venv to PATH so entry points are found
|
||||
ENV PATH="/app/.venv/bin:$PATH"
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "turnstone"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
description = "Multi-node AI orchestration platform with tool use, agent routing, and cluster simulation."
|
||||
readme = "README.md"
|
||||
license = "BUSL-1.1"
|
||||
@@ -51,7 +51,7 @@ console = ["redis>=7.2", "croniter>=3.0"]
|
||||
sim = ["redis>=7.2"]
|
||||
anthropic = ["anthropic>=0.39"]
|
||||
postgres = ["psycopg[binary]>=3.2"]
|
||||
ddg = ["duckduckgo-search>=8.0"]
|
||||
ddg = ["ddgs>=9.0"]
|
||||
discord = ["discord.py>=2.4", "redis>=7.2"]
|
||||
all = ["turnstone[mq,console,sim,anthropic,postgres,discord,ddg]"]
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class TestTavilyClient:
|
||||
|
||||
class TestDuckDuckGoClient:
|
||||
def test_integration_via_mock_ddgs(self):
|
||||
"""Patch the duckduckgo_search import inside DuckDuckGoClient.search."""
|
||||
"""Patch the ddgs import inside DuckDuckGoClient.search."""
|
||||
mock_ddgs = MagicMock()
|
||||
mock_ddgs.__enter__ = MagicMock(return_value=mock_ddgs)
|
||||
mock_ddgs.__exit__ = MagicMock(return_value=False)
|
||||
@@ -93,7 +93,7 @@ class TestDuckDuckGoClient:
|
||||
]
|
||||
mock_module = MagicMock()
|
||||
mock_module.DDGS.return_value = mock_ddgs
|
||||
with patch.dict("sys.modules", {"duckduckgo_search": mock_module}):
|
||||
with patch.dict("sys.modules", {"ddgs": mock_module}):
|
||||
client = DuckDuckGoClient(timeout=10)
|
||||
result = client.search("test query", max_results=3)
|
||||
mock_ddgs.text.assert_called_once_with("test query", max_results=3)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""turnstone - Multi-node AI orchestration platform with tool use, agent routing, and cluster simulation."""
|
||||
|
||||
__version__ = "0.8.5"
|
||||
__version__ = "0.8.6"
|
||||
|
||||
@@ -2803,7 +2803,7 @@ class ChatSession:
|
||||
"needs_approval": False,
|
||||
"error": (
|
||||
"Error: No web search backend available. "
|
||||
"Install duckduckgo-search (`pip install duckduckgo-search`), "
|
||||
"Install the ddg extra (`pip install turnstone[ddg]`), "
|
||||
"configure a Tavily API key, or set tools.web_search_backend."
|
||||
),
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
``web_search`` is an abstract capability with swappable clients:
|
||||
|
||||
* **TavilyClient** — paid, high quality, requires API key
|
||||
* **DuckDuckGoClient** — free, no API key, uses ``duckduckgo-search``
|
||||
* **DuckDuckGoClient** — free, no API key, uses ``ddgs``
|
||||
|
||||
Auto-detection (default): Tavily if key present, else DDG if installed,
|
||||
else ``None`` (tool removed from tool list).
|
||||
@@ -63,7 +63,7 @@ class DuckDuckGoClient:
|
||||
self._timeout = timeout
|
||||
|
||||
def search(self, query: str, max_results: int = 5, **kwargs: Any) -> str:
|
||||
from duckduckgo_search import DDGS # type: ignore[import-not-found]
|
||||
from ddgs import DDGS # type: ignore[import-not-found]
|
||||
|
||||
with DDGS(timeout=int(self._timeout)) as ddgs:
|
||||
raw = list(ddgs.text(query, max_results=max_results))
|
||||
@@ -130,9 +130,9 @@ def _format_ddg(results: list[dict[str, Any]], query: str) -> str:
|
||||
|
||||
|
||||
def _ddg_available() -> bool:
|
||||
"""Check if duckduckgo-search is installed."""
|
||||
"""Check if the ddgs package is installed."""
|
||||
try:
|
||||
import duckduckgo_search # noqa: F401
|
||||
import ddgs # noqa: F401
|
||||
|
||||
return True
|
||||
except ImportError:
|
||||
|
||||
@@ -604,6 +604,20 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605, upload-time = "2026-02-10T19:18:29.233Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ddgs"
|
||||
version = "9.11.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "lxml" },
|
||||
{ name = "primp" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/3c/0ea1d5685bb0cd3607556d76da30e64c158e5d86bb16a3555f232fe0a33f/ddgs-9.11.4.tar.gz", hash = "sha256:445e22d3ffa16f893bfb0a717593bb0f34d1ceb1df68b6ff4ec27b1a3cdf6941", size = 34798, upload-time = "2026-03-14T18:15:18.086Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/46/f9412fafdebd4eaba366b5336ee4987995ccc3db1bc450e865a34db5fb63/ddgs-9.11.4-py3-none-any.whl", hash = "sha256:62d4d05b25db5d225a727c0a2771ef40258c1d894582c73dad24c88bf90f918b", size = 43681, upload-time = "2026-03-14T18:15:17.063Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "discord-py"
|
||||
version = "2.7.1"
|
||||
@@ -635,20 +649,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "duckduckgo-search"
|
||||
version = "8.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "lxml" },
|
||||
{ name = "primp" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/10/ef/07791a05751e6cc9de1dd49fb12730259ee109b18e6d097e25e6c32d5617/duckduckgo_search-8.1.1.tar.gz", hash = "sha256:9da91c9eb26a17e016ea1da26235d40404b46b0565ea86d75a9f78cc9441f935", size = 22868, upload-time = "2025-07-06T15:30:59.73Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/db/72/c027b3b488b1010cf71670032fcf7e681d44b81829d484bb04e31a949a8d/duckduckgo_search-8.1.1-py3-none-any.whl", hash = "sha256:f48adbb06626ee05918f7e0cef3a45639e9939805c4fc179e68c48a12f1b5062", size = 18932, upload-time = "2025-07-06T15:30:58.339Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.8.0"
|
||||
@@ -2322,7 +2322,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "turnstone"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "alembic" },
|
||||
@@ -2345,8 +2345,8 @@ dependencies = [
|
||||
all = [
|
||||
{ name = "anthropic" },
|
||||
{ name = "croniter" },
|
||||
{ name = "ddgs" },
|
||||
{ name = "discord-py" },
|
||||
{ name = "duckduckgo-search" },
|
||||
{ name = "psycopg", extra = ["binary"] },
|
||||
{ name = "redis" },
|
||||
]
|
||||
@@ -2358,7 +2358,7 @@ console = [
|
||||
{ name = "redis" },
|
||||
]
|
||||
ddg = [
|
||||
{ name = "duckduckgo-search" },
|
||||
{ name = "ddgs" },
|
||||
]
|
||||
dev = [
|
||||
{ name = "mypy" },
|
||||
@@ -2391,8 +2391,8 @@ requires-dist = [
|
||||
{ name = "bcrypt", specifier = ">=4.0" },
|
||||
{ name = "croniter", marker = "extra == 'console'", specifier = ">=3.0" },
|
||||
{ name = "croniter", marker = "extra == 'test'", specifier = ">=3.0" },
|
||||
{ name = "ddgs", marker = "extra == 'ddg'", specifier = ">=9.0" },
|
||||
{ name = "discord-py", marker = "extra == 'discord'", specifier = ">=2.4" },
|
||||
{ name = "duckduckgo-search", marker = "extra == 'ddg'", specifier = ">=8.0" },
|
||||
{ name = "httpx", specifier = ">=0.28" },
|
||||
{ name = "httpx-sse", specifier = ">=0.4" },
|
||||
{ name = "mcp", specifier = ">=1.6" },
|
||||
|
||||
Reference in New Issue
Block a user