Skip to content

Commit c141de9

Browse files
refactor: remove direct REST client and direct mode fallback
1 parent d0e9fd0 commit c141de9

7 files changed

Lines changed: 27 additions & 392 deletions

File tree

ARCHITECTURE.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,6 @@ The SDK leverages several object-oriented and functional design patterns:
5656
### 5. Background Trigger Pattern (`triggers.rs`)
5757
- **Trigger Trait**: Defines asynchronous background tasks (such as status polling, listener intervals, etc.) that can interact with the connection session concurrently.
5858
- **TriggerRunner**: Coordinates and spawns registered triggers in separate tasks when the agent session starts.
59-
60-
### 6. Direct Gemini Client (`direct.rs`)
61-
- **GeminiDirectClient**: A transport-agnostic client that constructs REST request payloads and parses responses directly from the Gemini API. Bypasses the `localharness` and WebSocket loop entirely. It is helpful for environments where TCP/WebSocket or spawning subprocesses is not possible (e.g. lightweight WASI components).
62-
6359
---
6460

6561
## Component Details

README.md

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,8 @@ cargo leptos serve
178178
179179
### Leptos + Spin/WASI (Edge)
180180
181-
For edge/serverless deployments on [Fermyon Spin](https://www.fermyon.com/spin). Since Spin components cannot make outbound TCP/WebSocket connections, two modes are supported:
181+
For edge/serverless deployments on [Fermyon Spin](https://www.fermyon.com/spin). Since Spin components cannot make outbound TCP/WebSocket connections directly, this target runs in **Sidecar Mode**, where the component communicates via HTTP with a native runner process (`agent_server`):
182182
183-
**Sidecar Mode (Full SDK):**
184183
```sh
185184
# Terminal 1: Start the agent sidecar
186185
cd examples/agent_server
@@ -192,32 +191,8 @@ spin build --up
192191
# Open http://localhost:3000
193192
```
194193
195-
**Direct Mode (Lightweight, no sidecar):**
196-
```sh
197-
cd examples/leptos_ssr_axum
198-
spin build --up --variable gemini_api_key="your-key"
199-
```
200-
201194
> See [`examples/README.md`](examples/README.md) for detailed architecture diagrams and configuration options.
202195
203-
### GeminiDirectClient
204-
205-
For environments where TCP/WebSocket is unavailable (e.g. WASI, embedded), the SDK provides a transport-agnostic Gemini API client that builds HTTP request payloads and parses responses — the caller provides the HTTP transport:
206-
207-
```rust
208-
use antigravity_sdk_rust::direct::{GeminiDirectClient, ChatEntry};
209-
use antigravity_sdk_rust::types::GeminiConfig;
210-
211-
let client = GeminiDirectClient::new(&GeminiConfig::default())
212-
.with_system_instruction("You are a helpful assistant.".to_string());
213-
214-
// Build the request (URL, headers, body) — send via your HTTP client
215-
let request = client.build_request("your-api-key", "Hello!", &[]).unwrap();
216-
217-
// Parse the response
218-
let text = GeminiDirectClient::parse_response(&response_bytes).unwrap();
219-
```
220-
221196
## Local Development
222197
223198
This project uses [just](https://github.com/casey/just) to manage development tasks.

examples/README.md

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,7 @@ Browser → Axum Server (native)
5959

6060
**Path:** `leptos_ssr_axum/`
6161

62-
A chat application that runs as a [Spin](https://www.fermyon.com/spin) WebAssembly component. Since Spin components cannot make outbound TCP/WebSocket connections (only `wasi:http`), this example supports **two modes**:
63-
64-
#### Mode A: Sidecar Mode (Full SDK — Recommended)
65-
66-
Uses the [`agent_server`](#4-agent-server-sidecar) sidecar to get full SDK features. The Spin component calls the sidecar via HTTP.
62+
A chat application that runs as a [Spin](https://www.fermyon.com/spin) WebAssembly component. Since Spin components cannot make outbound TCP/WebSocket connections (only `wasi:http`), this example runs in **Sidecar Mode** using the [`agent_server`](#4-agent-server-sidecar) sidecar to get full SDK features (harness, tools, hooks, and policies).
6763

6864
```sh
6965
# Terminal 1: Start the sidecar (runs full SDK + localharness)
@@ -87,28 +83,6 @@ Browser → Spin Component (WASI)
8783
└─ KV Store (chat history)
8884
```
8985

90-
#### Mode B: Direct Mode (Lightweight, No Sidecar)
91-
92-
Calls the Gemini API directly using the SDK's `GeminiDirectClient`. No localharness or sidecar needed. Simpler to deploy but without tools, hooks, or policies.
93-
94-
```sh
95-
cd examples/leptos_ssr_axum
96-
spin build --up --variable gemini_api_key="your-key"
97-
```
98-
99-
**Architecture:**
100-
```
101-
Browser → Spin Component (WASI)
102-
103-
├─ GeminiDirectClient::build_request()
104-
├─ wasi::http → Gemini REST API
105-
├─ GeminiDirectClient::parse_response()
106-
107-
└─ KV Store (chat history)
108-
```
109-
110-
> **How does the mode get selected?** If the `gemini_api_key` Spin variable is set (non-empty), Direct Mode is used. Otherwise, Sidecar Mode is used (calls `agent_server_url`, which defaults to `http://127.0.0.1:8080`).
111-
11286
---
11387

11488
### 4. Agent Server (Sidecar)

examples/agent_server/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use axum::http::StatusCode;
2727
use axum::response::IntoResponse;
2828
use axum::routing::{get, post};
2929
use axum::{Json, Router};
30-
use serde::{Deserialize, Serialize};
30+
use serde::Deserialize;
3131
use std::sync::Arc;
3232
use tokio::sync::Mutex;
3333
use tower_http::cors::{Any, CorsLayer};

examples/leptos_ssr_axum/src/app.rs

Lines changed: 24 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -364,20 +364,14 @@ fn send_wasi_http_post(
364364

365365
Ok((status, resp_bytes))
366366
}
367-
368367
/// Send a message via the antigravity-sdk-rust Agent sidecar server (full SDK)
369-
/// or fall back to `GeminiDirectClient` (direct Gemini API) if `gemini_api_key` is set.
370-
///
371-
/// **Sidecar mode** (default): calls `POST /chat` on the `agent_server` binary which
372-
/// runs the full SDK with localharness, tools, hooks, and policies.
373368
///
374-
/// **Direct mode** (fallback): if `gemini_api_key` is provided in Spin variables,
375-
/// calls the Gemini API directly using `GeminiDirectClient`.
369+
/// Calls `POST /chat` on the `agent_server` binary which runs the full SDK with
370+
/// localharness, tools, hooks, and policies.
376371
#[server(prefix = "/api")]
377372
pub async fn send_message(message: String) -> Result<ChatMessage, ServerFnError<String>> {
378373
let agent_server_url = spin_sdk::variables::get("agent_server_url")
379374
.unwrap_or_else(|_| "http://127.0.0.1:8080".to_string());
380-
let gemini_api_key = spin_sdk::variables::get("gemini_api_key").unwrap_or_default();
381375

382376
// Load chat history from KV store
383377
let store = spin_sdk::key_value::Store::open_default().map_err(|e| e.to_string())?;
@@ -386,82 +380,34 @@ pub async fn send_message(message: String) -> Result<ChatMessage, ServerFnError<
386380
_ => Vec::new(),
387381
};
388382

389-
let text = if !gemini_api_key.trim().is_empty() {
390-
// ── Direct Mode: GeminiDirectClient (no sidecar needed) ──
391-
use antigravity_sdk_rust::direct::{ChatEntry, GeminiDirectClient};
392-
use antigravity_sdk_rust::types::GeminiConfig;
393-
394-
let chat_history: Vec<ChatEntry> = history
395-
.iter()
396-
.map(|msg| ChatEntry {
397-
role: if msg.role == "user" {
398-
"user".to_string()
399-
} else {
400-
"model".to_string()
401-
},
402-
content: msg.content.clone(),
403-
})
404-
.collect();
405-
406-
let mut gemini_config = GeminiConfig::default();
407-
gemini_config.models.default.name = "gemini-3.5-flash".to_string();
408-
409-
let client = GeminiDirectClient::new(&gemini_config).with_system_instruction(
410-
"You are a helpful AI assistant in a chat interface. \
411-
Keep responses concise and conversational. \
412-
Use markdown formatting when helpful."
413-
.to_string(),
414-
);
415-
416-
let request = client
417-
.build_request(gemini_api_key.trim(), &message, &chat_history)
418-
.map_err(|e| e.to_string())?;
419-
420-
let (status, resp_bytes) = send_wasi_http_post(
421-
&request.scheme,
422-
&request.authority,
423-
&request.path,
424-
&request.headers,
425-
&request.body,
426-
)?;
427-
428-
if status != 200 {
429-
return Err(ServerFnError::ServerError(
430-
GeminiDirectClient::parse_error(status, &resp_bytes),
431-
));
432-
}
433-
434-
GeminiDirectClient::parse_response(&resp_bytes).map_err(|e| e.to_string())?
435-
} else {
436-
// ── Sidecar Mode: Full SDK via agent_server ──
437-
let body = serde_json::to_vec(&serde_json::json!({ "message": message }))
438-
.map_err(|e| e.to_string())?;
383+
// ── Sidecar Mode: Full SDK via agent_server ──
384+
let body = serde_json::to_vec(&serde_json::json!({ "message": message }))
385+
.map_err(|e| e.to_string())?;
439386

440-
let headers = vec![("content-type".to_string(), b"application/json".to_vec())];
387+
let headers = vec![("content-type".to_string(), b"application/json".to_vec())];
441388

442-
// Parse the sidecar URL to extract scheme/authority
443-
let (scheme, authority) = parse_url_parts(&agent_server_url);
389+
// Parse the sidecar URL to extract scheme/authority
390+
let (scheme, authority) = parse_url_parts(&agent_server_url);
444391

445-
let (status, resp_bytes) =
446-
send_wasi_http_post(&scheme, &authority, "/chat", &headers, &body)?;
392+
let (status, resp_bytes) =
393+
send_wasi_http_post(&scheme, &authority, "/chat", &headers, &body)?;
447394

448-
if status != 200 {
449-
let err_text = String::from_utf8_lossy(&resp_bytes);
450-
return Err(ServerFnError::ServerError(format!(
451-
"Agent sidecar error ({}): {}",
452-
status, err_text
453-
)));
454-
}
395+
if status != 200 {
396+
let err_text = String::from_utf8_lossy(&resp_bytes);
397+
return Err(ServerFnError::ServerError(format!(
398+
"Agent sidecar error ({}): {}",
399+
status, err_text
400+
)));
401+
}
455402

456-
// Parse sidecar response: { "text": "...", "conversation_id": "..." }
457-
let resp_json: serde_json::Value =
458-
serde_json::from_slice(&resp_bytes).map_err(|e| e.to_string())?;
403+
// Parse sidecar response: { "text": "...", "conversation_id": "..." }
404+
let resp_json: serde_json::Value =
405+
serde_json::from_slice(&resp_bytes).map_err(|e| e.to_string())?;
459406

460-
resp_json["text"]
461-
.as_str()
462-
.unwrap_or("(No response)")
463-
.to_string()
464-
};
407+
let text = resp_json["text"]
408+
.as_str()
409+
.unwrap_or("(No response)")
410+
.to_string();
465411

466412
let now = std::time::SystemTime::now()
467413
.duration_since(std::time::UNIX_EPOCH)

0 commit comments

Comments
 (0)