Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion crates/goose-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ required-features = ["uniffi"]

[features]
default = []
uniffi = ["dep:uniffi", "dep:thiserror"]
uniffi = [
"dep:uniffi",
"dep:thiserror",
"dep:anyhow",
"dep:goose-providers",
"dep:futures",
"dep:serde_json",
"dep:tokio",
]

[dependencies]
goose-sdk-types = { path = "../goose-sdk-types" }
Expand All @@ -28,6 +36,11 @@ agent-client-protocol-schema = { workspace = true }

uniffi = { version = "0.31", features = ["cli"], optional = true }
thiserror = { version = "2", optional = true }
goose-providers = { version = "1.39.0", path = "../goose-providers", features = ["rustls-tls"], optional = true }
futures = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
tokio = { workspace = true, features = ["rt-multi-thread", "sync"], optional = true }
anyhow = { workspace = true, optional = true }

[dev-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "process", "io-std", "io-util"] }
Expand Down
11 changes: 5 additions & 6 deletions crates/goose-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ The bindings layer for Goose. It houses the shared types used for both ACP and
SDK access, and exposes a cross-language version of the Goose API.

With `--features uniffi` the crate compiles to native bindings for Python and
Kotlin (namespace `aaif_goose` / `aaif.goose`). The published surface is
currently a `ping` -> `pong` stub in `src/bindings.rs` — the scaffold for the
real implementation.
Kotlin (namespace `goose` / `io.aaif.goose`). The UniFFI surface currently lets
callers construct declarative providers from JSON and stream provider
completions.

```bash
just python # build bindings + run examples/uniffi/ping.py
just kotlin # build bindings + run examples/uniffi/Ping.kt
just python # build bindings + run examples/uniffi/provider.py
just kotlin # build bindings + run examples/uniffi/Provider.kt
```

Both print `pong: aaif.io`.
30 changes: 30 additions & 0 deletions crates/goose-sdk/examples/deepseek.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "deepseek",
"engine": "openai",
"display_name": "DeepSeek",
"description": "Custom DeepSeek provider",
"api_key_env": "DEEPSEEK_API_KEY",
"base_url": "https://api.deepseek.com",
"models": [
{
"name": "deepseek-chat",
"context_limit": 128000,
"input_token_cost": null,
"output_token_cost": null,
"currency": null,
"supports_cache_control": null
},
{
"name": "deepseek-reasoner",
"context_limit": 128000,
"input_token_cost": null,
"output_token_cost": null,
"currency": null,
"supports_cache_control": null
}
],
"headers": null,
"timeout_seconds": null,
"preserves_thinking": true,
"supports_streaming": true
}
9 changes: 0 additions & 9 deletions crates/goose-sdk/examples/uniffi/Ping.kt

This file was deleted.

31 changes: 31 additions & 0 deletions crates/goose-sdk/examples/uniffi/Provider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package aaif.example

import io.aaif.goose.DeclarativeProvider
import io.aaif.goose.MessageRole
import io.aaif.goose.ProviderMessage
import io.aaif.goose.ProviderModelConfig
import java.nio.file.Paths

fun main() {
val examplesDir = Paths.get("crates/goose-sdk/examples")
val provider = DeclarativeProvider.fromJson(examplesDir.resolve("deepseek.json").toFile().readText())
val model = ProviderModelConfig(modelName = "deepseek-v4-flash")
val messages = listOf(
ProviderMessage(
role = MessageRole.USER,
text = "what is the capital of France?",
),
)
val stream = provider.stream(
model,
"You are a knowledgable geography expert",
messages,
)

while (true) {
val chunk = stream.next() ?: break
chunk.text?.let { print(it) }
chunk.usageJson?.let { println("\nusage: $it") }
}
println()
}
53 changes: 53 additions & 0 deletions crates/goose-sdk/examples/uniffi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# UniFFI examples

These examples exercise the in-process Goose SDK UniFFI bindings from Python and Kotlin.

## Prerequisites

```bash
source bin/activate-hermit
export DEEPSEEK_API_KEY=...
```

## Generate bindings

Regenerate the Python and Kotlin bindings before running the examples:

```bash
just --justfile crates/goose-sdk/justfile _generate python
just --justfile crates/goose-sdk/justfile _generate kotlin
```

This writes generated bindings and the debug native library under `crates/goose-sdk/generated/`.

## Python provider example

```bash
DYLD_LIBRARY_PATH=target/debug LD_LIBRARY_PATH=target/debug \
uv run --script crates/goose-sdk/examples/uniffi/provider.py
```

## Kotlin provider example

Download JNA if it is not already present:

```bash
curl -sSL -o crates/goose-sdk/examples/uniffi/jna.jar \
https://repo1.maven.org/maven2/net/java/dev/jna/jna/5.14.0/jna-5.14.0.jar
```

Compile and run:

```bash
kotlinc -cp crates/goose-sdk/examples/uniffi/jna.jar -nowarn \
crates/goose-sdk/generated/io/aaif/goose/goose.kt \
crates/goose-sdk/examples/uniffi/Provider.kt \
-include-runtime -d crates/goose-sdk/examples/uniffi/provider.jar

java -Djna.library.path=target/debug \
--enable-native-access=ALL-UNNAMED \
-cp crates/goose-sdk/examples/uniffi/provider.jar:crates/goose-sdk/examples/uniffi/jna.jar \
aaif.example.ProviderKt
```

On Linux, use the same command; `LD_LIBRARY_PATH=target/debug` can also be set if needed. On macOS, `-Djna.library.path=target/debug` is usually enough, but `DYLD_LIBRARY_PATH=target/debug` can also be set if JNA cannot find `libgoose_sdk.dylib`.
21 changes: 0 additions & 21 deletions crates/goose-sdk/examples/uniffi/ping.py

This file was deleted.

38 changes: 38 additions & 0 deletions crates/goose-sdk/examples/uniffi/provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env -S uv run --script
"""Goose SDK demo: build a declarative provider and stream a completion."""
import json
import sys
from pathlib import Path

HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent.parent / "generated"))

from goose import ( # noqa: E402
DeclarativeProvider,
MessageRole,
ProviderMessage,
ProviderModelConfig,
)


def main() -> None:
provider = DeclarativeProvider.from_json((HERE.parent / "deepseek.json").read_text())
model = ProviderModelConfig(model_name="deepseek-v4-flash")
messages = [ProviderMessage(role=MessageRole.USER, text="what is the capital of France?")]
stream = provider.stream(
model,
"You are a knowledgable geography expert",
messages,
)

while chunk := stream.next():
if chunk.text:
print(chunk.text, end="")
if chunk.usage_json:
usage = json.loads(chunk.usage_json)
print(f"\nusage: {usage}")
print()


if __name__ == "__main__":
main()
10 changes: 5 additions & 5 deletions crates/goose-sdk/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ _generate lang: _build

python: (_generate "python")
DYLD_LIBRARY_PATH={{lib_dir}} LD_LIBRARY_PATH={{lib_dir}} \
python3 {{examples_dir}}/ping.py
uv run --script {{examples_dir}}/provider.py

kotlin: (_generate "kotlin")
@if [ ! -f {{examples_dir}}/jna.jar ]; then \
curl -sSL -o {{examples_dir}}/jna.jar \
https://repo1.maven.org/maven2/net/java/dev/jna/jna/5.14.0/jna-5.14.0.jar; \
fi
kotlinc -cp {{examples_dir}}/jna.jar -nowarn \
{{gen_dir}}/aaif/goose/aaif_goose.kt \
{{examples_dir}}/Ping.kt \
-include-runtime -d {{examples_dir}}/ping.jar 2>/dev/null
{{gen_dir}}/io/aaif/goose/goose.kt \
{{examples_dir}}/Provider.kt \
-include-runtime -d {{examples_dir}}/provider.jar 2>/dev/null
java -Djna.library.path={{lib_dir}} \
--enable-native-access=ALL-UNNAMED \
-cp {{examples_dir}}/ping.jar:{{examples_dir}}/jna.jar aaif.example.PingKt
-cp {{examples_dir}}/provider.jar:{{examples_dir}}/jna.jar aaif.example.ProviderKt
Loading
Loading