fere-sdk 0.3.0.dev23__tar.gz → 0.3.1.dev27__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.
@@ -0,0 +1,52 @@
1
+ *.log
2
+ *.pyc
3
+ __pycache__
4
+
5
+ # Devcontainer local env (contains secrets)
6
+ .devcontainer/.env.local
7
+ .devcontainer/.env.local.override
8
+ .devcontainer/.env.gitpod
9
+
10
+ # Bruno gateway collection (GATEWAY_AGENT_ID / GATEWAY_PRIVATE_KEY_B64)
11
+ bruno_specs/gateway-service/.env
12
+
13
+ # Git worktrees
14
+ .worktrees/
15
+
16
+ # tmp/ — keep .gitkeep, ignore all else
17
+ tmp/*
18
+ !tmp/.gitkeep
19
+ coverage.xml
20
+
21
+ # triage-rum session state (contains stack traces / error data)
22
+ .claude/skills/triage-rum/triage-session.json
23
+
24
+ # Claude Code per-session scheduler lock (local runtime state)
25
+ .claude/scheduled_tasks.lock
26
+
27
+ # Ralph/autonomous loop runtime logs
28
+ dev/logs/*
29
+ !dev/logs/.gitignore
30
+
31
+ # VSCode per-user IDE settings
32
+ .vscode/settings.json
33
+
34
+ # Poetry local venv preference (in-project .venv) — dev-only, per-service
35
+ **/poetry.toml
36
+
37
+ # Cursor SEO skill — regenerated report JSON (not source)
38
+ .cursor/skills/seo-agent/artifacts/
39
+
40
+ # Skills installed locally by the skills package manager (see skills-lock.json)
41
+ .claude/skills/stripe-best-practices
42
+ .claude/skills/stripe-projects
43
+ .claude/skills/upgrade-stripe
44
+ .agents/
45
+
46
+ # Local GEO upstream reference (optional clone; not part of the product tree)
47
+ geo-seo/
48
+
49
+ # Eval output
50
+ compare_results.json
51
+ .secrets
52
+ wallet_service/celerybeat-schedule-*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fere-sdk
3
- Version: 0.3.0.dev23
3
+ Version: 0.3.1.dev27
4
4
  Summary: Python SDK for the FereAI Gateway API
5
5
  Author-email: Fere AI <info@fere.ai>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fere-sdk"
3
- version = "0.3.0.dev23"
3
+ version = "0.3.1.dev27"
4
4
  description = "Python SDK for the FereAI Gateway API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -162,3 +162,20 @@ class AuthManager:
162
162
  """Invalidate the cached token to force a refresh."""
163
163
  self._token = None
164
164
  self._token_expires_at = 0
165
+
166
+
167
+ class StaticTokenAuthManager:
168
+ """Auth provider for callers that already have a bearer token."""
169
+
170
+ def __init__(self, token: str) -> None:
171
+ self._token = token.removeprefix("Bearer ").strip()
172
+
173
+ async def ensure_token(self, client: httpx.AsyncClient) -> str:
174
+ return self._token
175
+
176
+ def auth_headers(self) -> dict[str, str]:
177
+ return {"Authorization": f"Bearer {self._token}"}
178
+
179
+ def clear_token(self) -> None:
180
+ """Static tokens cannot be refreshed by the SDK."""
181
+ return None
@@ -29,6 +29,27 @@ from .models import (
29
29
  DEFAULT_TIMEOUT = 120
30
30
 
31
31
 
32
+ class ToolsClient:
33
+ """Convenience namespace for tool discovery and invocation."""
34
+
35
+ def __init__(self, api: FereAPI) -> None:
36
+ self._api = api
37
+
38
+ async def list(self, category: str | None = None) -> dict:
39
+ return await self._api.list_tools(category=category)
40
+
41
+ async def describe(self, name: str) -> dict:
42
+ return await self._api.describe_tool(name)
43
+
44
+ async def call(
45
+ self,
46
+ name: str,
47
+ args: dict[str, Any] | None = None,
48
+ chat_id: str | None = None,
49
+ ) -> dict:
50
+ return await self._api.call_tool(name, args=args, chat_id=chat_id)
51
+
52
+
32
53
  class FereClient:
33
54
  """High-level async client that blocks until tasks complete.
34
55
 
@@ -38,6 +59,7 @@ class FereClient:
38
59
 
39
60
  def __init__(self, api: FereAPI) -> None:
40
61
  self._api = api
62
+ self.tools = ToolsClient(api)
41
63
 
42
64
  @classmethod
43
65
  async def create(
@@ -59,6 +81,15 @@ class FereClient:
59
81
  await api.authenticate()
60
82
  return cls(api)
61
83
 
84
+ @classmethod
85
+ def from_token(
86
+ cls,
87
+ token: str,
88
+ base_url: str = "https://api.fereai.xyz",
89
+ ) -> FereClient:
90
+ """Create a client from an existing Clerk or AgentDoor bearer token."""
91
+ return cls(FereAPI.from_token(token, base_url=base_url))
92
+
62
93
  async def close(self) -> None:
63
94
  await self._api.close()
64
95
 
@@ -9,7 +9,7 @@ from typing import Any
9
9
 
10
10
  import httpx
11
11
 
12
- from .auth import AuthManager
12
+ from .auth import AuthManager, StaticTokenAuthManager
13
13
  from .models import (
14
14
  ChatEvent,
15
15
  CreditsInfo,
@@ -43,17 +43,35 @@ class FereAPI:
43
43
 
44
44
  def __init__(
45
45
  self,
46
- agent_name: str,
46
+ agent_name: str = "fere-sdk",
47
47
  base_url: str = DEFAULT_BASE_URL,
48
48
  key_path: str | None = None,
49
49
  timeout: int = 120,
50
+ bearer_token: str | None = None,
50
51
  ) -> None:
51
52
  self.base_url = base_url.rstrip("/")
52
- self._auth = AuthManager(
53
- agent_name, base_url=self.base_url, key_path=key_path
54
- )
53
+ if bearer_token:
54
+ self._auth = StaticTokenAuthManager(bearer_token)
55
+ else:
56
+ self._auth = AuthManager(
57
+ agent_name, base_url=self.base_url, key_path=key_path
58
+ )
55
59
  self._client = httpx.AsyncClient(timeout=timeout)
56
60
 
61
+ @classmethod
62
+ def from_token(
63
+ cls,
64
+ token: str,
65
+ base_url: str = DEFAULT_BASE_URL,
66
+ timeout: int = 120,
67
+ ) -> FereAPI:
68
+ """Create a low-level client from an existing bearer token."""
69
+ return cls(
70
+ base_url=base_url,
71
+ timeout=timeout,
72
+ bearer_token=token,
73
+ )
74
+
57
75
  async def __aenter__(self):
58
76
  return self
59
77
 
@@ -195,6 +213,31 @@ class FereAPI:
195
213
  """GET /v1/chat/threads/{thread_id}"""
196
214
  return await self._authed_get(f"/v1/chat/threads/{thread_id}")
197
215
 
216
+ # ----------------------------------------------------------
217
+ # Tools
218
+ # ----------------------------------------------------------
219
+
220
+ async def list_tools(self, category: str | None = None) -> dict:
221
+ """GET /v1/tools"""
222
+ params = {"category": category} if category else None
223
+ return await self._authed_get("/v1/tools", params=params)
224
+
225
+ async def describe_tool(self, name: str) -> dict:
226
+ """GET /v1/tools/{name}"""
227
+ return await self._authed_get(f"/v1/tools/{name}")
228
+
229
+ async def call_tool(
230
+ self,
231
+ name: str,
232
+ args: dict[str, Any] | None = None,
233
+ chat_id: str | None = None,
234
+ ) -> dict:
235
+ """POST /v1/tools/{name}/invoke"""
236
+ return await self._authed_post(
237
+ f"/v1/tools/{name}/invoke",
238
+ {"args": args or {}, "chat_id": chat_id},
239
+ )
240
+
198
241
  # ----------------------------------------------------------
199
242
  # Trading
200
243
  # ----------------------------------------------------------
@@ -1,21 +0,0 @@
1
- *.log
2
- *.pyc
3
- __pycache__
4
-
5
- # Devcontainer local env (contains secrets)
6
- .devcontainer/.env.local
7
- .devcontainer/.env.gitpod
8
-
9
- # Git worktrees
10
- .worktrees/
11
-
12
- # tmp/ — keep .gitkeep, ignore all else
13
- tmp/*
14
- !tmp/.gitkeep
15
- coverage.xml
16
-
17
- # triage-rum session state (contains stack traces / error data)
18
- .claude/skills/triage-rum/triage-session.json
19
-
20
- # Eval output
21
- compare_results.json
File without changes