Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ jobs:
${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib
run: bash scripts/retry-command.sh make codegen-rn
- uses: actions/upload-artifact@v4
if: github.event_name != 'pull_request'
with:
name: rn-bindings
path: out/rn/
Expand Down Expand Up @@ -140,6 +141,7 @@ jobs:
- name: Build WASM ESM package
run: make build-wasm
- uses: actions/upload-artifact@v4
if: github.event_name != 'pull_request'
with:
name: web-bindings
path: out/web/
Expand All @@ -163,6 +165,7 @@ jobs:
- name: Codegen — Flutter/Dart
run: make codegen-flutter
- uses: actions/upload-artifact@v4
if: github.event_name != 'pull_request'
with:
name: flutter-bindings
path: out/flutter/
16 changes: 2 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -276,20 +276,8 @@ jobs:
files: ["src/", "android/", "ios/", "README.md"],
license: "Apache-2.0"
}' > assembly/package.json
- name: Write README.md
run: |
cat > assembly/README.md <<'EOF'
# edge-intelligence-sdk

Generated Web and React Native bindings for the Edge Intelligence SDK.

This package contains the WASM/TypeScript browser surface, React Native
TypeScript bindings, and native Android/iOS libraries assembled by the
release pipeline.

Source, crate documentation, and release notes live in the Edge
Intelligence repository.
EOF
- name: Copy README.md
run: cp packaging/npm/README.md assembly/README.md
- name: Assert npm package ships README.md
run: bash scripts/assert-release-readmes.sh npm assembly
- uses: actions/upload-artifact@v4
Expand Down
26 changes: 13 additions & 13 deletions Cargo.lock

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

22 changes: 11 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ exclude = [

[workspace.package]
edition = "2021"
version = "0.3.9"
version = "0.3.10"
license = "Apache-2.0"
rust-version = "1.96"
repository = "Edge-Native LLM SDK"
Expand All @@ -48,16 +48,16 @@ repository = "Edge-Native LLM SDK"
# This is the single source of truth for internal dep version requirements;
# member crates reference them with `{ workspace = true }` and add no inline
# version fields. `cargo set-version --workspace <ver>` updates every entry.
el-core = { path = "crates/el-core", version = "0.3.9" }
el-memory = { path = "crates/el-memory", version = "0.3.9" }
el-telemetry = { path = "crates/el-telemetry", version = "0.3.9" }
el-provenance = { path = "crates/el-provenance", version = "0.3.9" }
el-safety = { path = "crates/el-safety", version = "0.3.9" }
el-runtime = { path = "crates/el-runtime", version = "0.3.9" }
el-grammar = { path = "crates/el-grammar", version = "0.3.9" }
el-provenance-ed25519 = { path = "crates/adapters/el-provenance-ed25519", version = "0.3.9" }
el-engine-candle = { path = "crates/adapters/el-engine-candle", version = "0.3.9" }
el-cloud = { path = "crates/adapters/el-cloud", version = "0.3.9" }
el-core = { path = "crates/el-core", version = "0.3.10" }
el-memory = { path = "crates/el-memory", version = "0.3.10" }
el-telemetry = { path = "crates/el-telemetry", version = "0.3.10" }
el-provenance = { path = "crates/el-provenance", version = "0.3.10" }
el-safety = { path = "crates/el-safety", version = "0.3.10" }
el-runtime = { path = "crates/el-runtime", version = "0.3.10" }
el-grammar = { path = "crates/el-grammar", version = "0.3.10" }
el-provenance-ed25519 = { path = "crates/adapters/el-provenance-ed25519", version = "0.3.10" }
el-engine-candle = { path = "crates/adapters/el-engine-candle", version = "0.3.10" }
el-cloud = { path = "crates/adapters/el-cloud", version = "0.3.10" }

[profile.release]
opt-level = 3
Expand Down
8 changes: 1 addition & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,7 @@ codegen-flutter:
' flutter_rust_bridge: ^$(FRB_VERSION)' \
> $(OUT)/flutter/pubspec.yaml
@cp LICENSE $(OUT)/flutter/LICENSE
@printf '%s\n' \
'# edge_intelligence' \
'' \
'Generated Flutter bindings for the Edge Intelligence SDK.' \
'' \
'The package contains Dart bindings generated with flutter_rust_bridge and native libraries assembled by the release pipeline.' \
> $(OUT)/flutter/README.md
@cp packaging/flutter/README.md $(OUT)/flutter/README.md
@printf '%s\n' \
'# Changelog' \
'' \
Expand Down
4 changes: 4 additions & 0 deletions crates/adapters/el-cloud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,15 @@ a clean completion), and errors carry only parse category/position/size —

## Usage

Use `CloudProvider` only as an explicit fallback or comparison path; the local
Qwen2.5 0.5B GGUF remains on device and is not sent to the provider.

```rust
use el_core::{ChatMessage, ChatRequest, CredentialRef, LlmProvider};
use el_cloud::CloudProvider;

let provider = CloudProvider::new();
let _local_qwen_model = "models/qwen2.5-0.5b-instruct-q4_k_m.gguf";

// The credential is resolved at runtime from the platform keystore — never embedded.
let req = ChatRequest::new("openai/gpt-4o", vec![ChatMessage::user("Hello!")])
Expand Down
15 changes: 10 additions & 5 deletions crates/adapters/el-engine-candle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,22 @@ Mismatched shapes are rejected at load time, not silently at inference.

## Usage

This is the direct Rust SDK path for a local Qwen2.5 0.5B instruct model.

```rust
use el_core::{ChatMessage, ChatRequest, LlmProvider};
use el_engine_candle::QwenChatProvider;

const QWEN_0_5B_GGUF: &str = "models/qwen2.5-0.5b-instruct-q4_k_m.gguf";
const QWEN_0_5B_TOKENIZER: &str = "models/qwen2.5-0.5b-instruct.tokenizer.json";

// Real on-device chat: a local Qwen2 GGUF + its tokenizer (no network egress).
let provider = QwenChatProvider::from_paths(
"models/qwen2.5-0.5b-instruct-q4_k_m.gguf",
"models/qwen2.5-0.5b-instruct.tokenizer.json",
)?;
let provider = QwenChatProvider::from_paths(QWEN_0_5B_GGUF, QWEN_0_5B_TOKENIZER)?;

let req = ChatRequest::new("local", vec![ChatMessage::user("What is the capital of France?")])
let req = ChatRequest::new(
"local/qwen2.5-0.5b-instruct-q4_k_m",
vec![ChatMessage::user("Summarize edge inference in one sentence.")],
)
.with_max_tokens(128);
let reply = provider.chat(&req)?;
println!("{}", reply.content);
Expand Down
63 changes: 59 additions & 4 deletions crates/adapters/el-ffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,74 @@ No `unsafe` (`#![forbid(unsafe_code)]`). The crate is `cdylib` + `staticlib` +

## Usage (Rust side)

Use the Qwen2.5 0.5B GGUF as the local model URI when constructing the facade.
Native package bindings use the same model path after copying the file into app
storage. For full Rust ChatML/tokenizer control, use
`el_engine_candle::QwenChatProvider` directly.

```rust
use el_ffi::EdgeLlm;

// Empty path → deterministic toy model; exercises the full binding layer.
let sdk = EdgeLlm::local(String::new())?;
let reply = sdk.ask("hello".into())?;
const QWEN_0_5B_GGUF: &str = "models/qwen2.5-0.5b-instruct-q4_k_m.gguf";

let sdk = EdgeLlm::local(QWEN_0_5B_GGUF.into())?;
let reply = sdk.ask("Summarize edge inference in one sentence.".into())?;
assert!(!reply.is_empty());

// Streaming (Flutter / closure form):
sdk.ask_stream("hi".into(), |fragment| print!("{fragment}"))?;
sdk.ask_stream("Give me two deployment tips.".into(), |fragment| print!("{fragment}"))?;
# Ok::<(), el_ffi::SdkError>(())
```

## Usage (npm / web)

The npm package exposes the wasm-bindgen browser surface. The local web path
currently exercises the generated API shape while Candle-on-wasm is being wired;
native React Native builds load the GGUF through the same package.

```ts
import init, { EdgeLlm } from "edge-intelligence-sdk";

await init();

const qwen05b = "/models/qwen2.5-0.5b-instruct-q4_k_m.gguf";
const sdk = new EdgeLlm(qwen05b);
const reply = sdk.ask_wasm("Summarize edge inference in one sentence.");

console.log(reply);
```

## Usage (React Native)

```ts
import { EdgeLlm } from "edge-intelligence-sdk";

const qwen05b = "/data/user/0/com.example.app/files/models/qwen2.5-0.5b-instruct-q4_k_m.gguf";
const sdk = EdgeLlm.local(qwen05b);

const reply = sdk.ask("Summarize edge inference in one sentence.");
let streamed = "";
sdk.ask_stream_cb("Give me two deployment tips.", {
on_token(token) {
streamed += token;
},
});
```

## Usage (pub.dev / Flutter)

```dart
import "package:edge_intelligence/edge_intelligence.dart";

final qwen05b = "/path/to/app/models/qwen2.5-0.5b-instruct-q4_k_m.gguf";
final sdk = await EdgeLlm.local(qwen05b);

final reply = await sdk.ask("Summarize edge inference in one sentence.");
await for (final token in sdk.askStream("Give me two deployment tips.")) {
stdout.write(token);
}
```

## Building the bindings

The Rust binding *surfaces* compile on the host; the cross-target builds and
Expand Down
7 changes: 6 additions & 1 deletion crates/adapters/el-grammar-llguidance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@ The HuggingFace `tokenizers::Tokenizer` is bridged to llguidance's `TokEnv` via

## Usage

Use the same Qwen2.5 0.5B tokenizer that the chat provider uses, so schema
masking and model decoding agree on token ids.

```rust
use el_grammar_llguidance::LlguidanceMasker;
use el_runtime::GrammarMasker;

let tokenizer = tokenizers::Tokenizer::from_file("tokenizer.json").unwrap();
let tokenizer = tokenizers::Tokenizer::from_file(
"models/qwen2.5-0.5b-instruct.tokenizer.json",
).unwrap();
let masker = LlguidanceMasker::from_tokenizer(&tokenizer, r#"{"type":"integer"}"#)?;

// In the decode loop, wired into the session's Ports:
Expand Down
10 changes: 9 additions & 1 deletion crates/adapters/el-provenance-ed25519/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,24 @@ do about it (issue a `LoadPermit`, or hard-stop). Pure-Rust dependency tree, no

## Usage

Use this adapter when a shipped Qwen2.5 0.5B GGUF is accompanied by a detached
ED25519 signature and a trusted provider public key.

```rust
use el_core::{ModelFormat, ModelId, ModelVersion};
use el_provenance::ModelArtifact;
use el_provenance_ed25519::Ed25519Verifier;
use std::path::Path;

let mut verifier = Ed25519Verifier::new();
verifier.register(/* public_key_id */ 1, trusted_public_key_bytes)?;

let qwen_path = Path::new("models/qwen2.5-0.5b-instruct-q4_k_m.gguf");
let model_bytes = std::fs::read(qwen_path)?;
let signature_bytes = std::fs::read(qwen_path.with_extension("gguf.sig"))?;

let mut artifact = ModelArtifact::new(ModelId(1), ModelVersion::new(0, 1, 0), ModelFormat::Gguf);
artifact.verify(&verifier, model_bytes, signature_bytes, 1);
artifact.verify(&verifier, &model_bytes, &signature_bytes, 1);

// Verified → a LoadPermit; tampered bytes, a forged signature, or an unknown
// key id → a hard error with no fallback.
Expand Down
11 changes: 10 additions & 1 deletion crates/el-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ These are enforced by the type system, not by convention:

## Usage

This is the crate-local part of a real local SDK call. In an app, the request
can be served by `el_engine_candle::QwenChatProvider::from_paths(...)` with
`models/qwen2.5-0.5b-instruct-q4_k_m.gguf` and
`models/qwen2.5-0.5b-instruct.tokenizer.json`; `el-core` only defines the
backend-agnostic contract.

```rust
use el_core::{ChatMessage, ChatRequest, LlmProvider, SessionConfig};

Expand All @@ -49,7 +55,10 @@ assert!(!cfg.hybrid_mode);

// A backend-agnostic request. `model` is a routing hint:
// "local"/"" → local engine, "openai/…", "anthropic/…", "ollama/…", "gemini/…"
let req = ChatRequest::new("local", vec![ChatMessage::user("Hello!")])
let req = ChatRequest::new(
"local/qwen2.5-0.5b-instruct-q4_k_m",
vec![ChatMessage::user("Summarize edge inference in one sentence.")],
)
.with_max_tokens(256)
.with_temperature(700); // 700 = 0.7 (milli to keep the type Eq-able)

Expand Down
Loading
Loading