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
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,47 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- New standalone `yup_shader_bundler` console tool (`cmake/tools/shader_bundler`): takes a `.vert` and `.frag` GLSL (v450 Vulkan dialect) pair on disk and produces a single `.ysl` bundle containing transpiled variants for all target languages (GLSL/ESSL/HLSL/MSL).
- New `yup_add_shader_bundle()` CMake helper (`cmake/yup_shader_bundler.cmake`): builds the `yup_shader_bundler` tool for the host once (cached in the global property `YUP_SHADER_BUNDLER_EXECUTABLE`), runs it at configure time to generate the `.ysl`, and embeds it into a linkable object library via `yup_add_embedded_binary_resources`. Works even when the outer build is cross-compiling, since the tool is built in its own host binary tree without forwarding the cross toolchain. Accepts an `OPTIONS` argument that forwards arbitrary extra flags verbatim to `yup_shader_bundler` (e.g. `--spirv-opt`, `--target-langs`, `-DNAME=VALUE`, `-I<dir>`).

### AI (`yup_ai`)

- New `yup_ai` module (`modules/yup_ai`): LLM client and AI integration classes depending on `yup_core` and `yup_events`.

#### LLM

- `LLMClient` (`yup_LLMClient.h`): abstract base for chat-completion backends with `complete()` and `completeStreaming()` methods, tool-call loop support via `runToolLoop()`, and structured output via `LLMSchema` JSON Schema or GBNF grammars.
- `LLMHttpClient` (`yup_LLMHttpClient.h`): HTTP transport for `LLMClient` with retry and timeout logic, handling streaming SSE and non-streaming JSON responses.
- `LLMClientFactory` (`yup_LLMClientFactory.h`): creates the correct `LLMHttpClient` subclass from `LLMClient::Options::provider`, with convenience factories for each provider.
- `LLMMessage` (`yup_LLMMessage.h`): chat message with four roles (system, user, assistant, tool), optional tool calls, and serialisation to/from OpenAI ChatML JSON.
- `LLMResponse` (`yup_LLMResponse.h`): parsed completion response with choices, token usage, tool-call extraction, streaming chunk accumulation, and error handling.
- `LLMTool` (`yup_LLMTool.h`): callable function descriptor with JSON Schema parameters and a local handler, serialised to OpenAI function-calling format.
- `LLMToolRegistry` (`yup_LLMToolRegistry.h`): thread-safe registry for `LLMTool` instances with snapshot, lookup, dispatch, and tools-array serialisation.
- `LLMSchema` (`yup_LLMSchema.h`): fluent builder for JSON Schema objects (`string`, `number`, `integer`, `boolean`, `array`, `object`, `oneOf`) used in structured-output requests across all providers.

#### LLM Providers

- `LLMOpenAIChatClient` (`yup_LLMOpenAIChatClient.h`): OpenAI Chat Completions API — also compatible with Ollama, DeepSeek, OpenRouter, and llama-server.
- `LLMOpenAIResponsesClient` (`yup_LLMOpenAIResponsesClient.h`): OpenAI Responses API (GPT-5+, reasoning models).
- `LLMAnthropicClient` (`yup_LLMAnthropicClient.h`): Anthropic Messages API (Claude models).
- `LLMGeminiClient` (`yup_LLMGeminiClient.h`): Google Gemini generateContent API.

#### Embeddings

- `EmbeddingModel` (`yup_EmbeddingModel.h`): OpenAI-compatible HTTP embedding model with `embed()` / `embedBatch()` and `cosineSimilarity()` helper.

#### MCP (Model Context Protocol)

- `MCPTypes` (`yup_MCPTypes.h`): JSON-RPC 2.0 request/response/error types, MCP capability flags, tool and resource definitions with `toVar` / `fromVar` serialisation.
- `MCPTransport` (`yup_MCPTransport.h`): abstract transport interface for JSON-RPC messages (stdio, HTTP/SSE, sockets, in-process).
- `MCPClient` (`yup_MCPClient.h`): synchronous MCP client with `initialize()` handshake, `listTools()` / `callTool()`, `listResources()` / `readResource()`, and tool-import bridge `registerToolsWith()`.
- `MCPServer` (`yup_MCPServer.h`): MCP server exposing local YUP tools and resources over a transport, with `registerTool()` / `registerResource()`, `start()` / `stop()`, and placeholder `startStdio()` / `startHttp()`.

#### Python Bindings

- Python bindings for `yup_ai` (`modules/yup_python/bindings/yup_YupAi_bindings.cpp`): exposes LLM client, provider, messages, tools, responses, MCP types, client, and server to Python via pybind11.

### Examples

- `SpinningCubeDemo` example (`examples/graphics`): rewritten to the new RHI shape — `GpuFrame` + `GpuCanvas::beginDraw` + `GpuRenderPass` for both the indexed cube draw and the separable two-pass blur (H+V sharing one `GpuFrame`), `isGpuAvailable()` capability probe, and live GLSL editing via `GpuPipeline::compileFromGlsl`. The default Lottie animation is now played back per-frame into an offscreen `GpuCanvas` (2D path) and sampled by the cube's fragment shader so the animation is texture-mapped onto every cube face.
- `AIDemo` example (`examples/graphics/source/examples/AI.h`): interactive demo for all four LLM providers (OpenAI Chat, OpenAI Responses, Anthropic, Gemini) with model and API key configuration, system prompt editing, streaming and non-streaming completion, tool calling, MCP server integration, and embedded text generation.

### Build System

Expand Down
98 changes: 98 additions & 0 deletions docs/ai/embedding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Embeddings

`EmbeddingModel` converts text to dense vector embeddings via any
OpenAI-compatible embeddings endpoint. Use it for semantic search, clustering,
recommendations, and retrieval-augmented generation (RAG).

## Creating a model

```cpp
#include <yup_ai/yup_ai.h>

yup::EmbeddingModel::Options opts;
opts.model = "text-embedding-3-small";
opts.baseUrl = "https://api.openai.com/v1";
opts.apiKey = "sk-...";

yup::EmbeddingModel embeddingModel (opts);
```

For Ollama, point `baseUrl` to your local server:

```cpp
yup::EmbeddingModel::Options opts;
opts.model = "nomic-embed-text";
opts.baseUrl = "http://localhost:11434/v1";
```

## Embedding text

### Single input

```cpp
auto embedding = embeddingModel.embed ("Machine learning is fascinating.");
DBG ("Dimensions: " << embedding.dimensions()); // e.g. 1536

// Access the vector
for (float v : embedding.values)
process (v);
```

### Batch input

```cpp
auto embeddings = embeddingModel.embedBatch ({
"What is a neural network?",
"How does backpropagation work?",
"The weather is nice today."
});

for (auto& e : embeddings)
DBG ("Index " << e.index << ": " << e.dimensions() << " dimensions");
```

## Similarity

Compute cosine similarity between two embeddings (range [-1, 1]):

```cpp
auto e1 = embeddingModel.embed ("artificial intelligence");
auto e2 = embeddingModel.embed ("machine learning");
auto e3 = embeddingModel.embed ("cooking recipes");

float scoreAIvsML = yup::EmbeddingModel::cosineSimilarity (e1, e2); // ~0.85
float scoreAIvsCooking = yup::EmbeddingModel::cosineSimilarity (e1, e3); // ~0.15
```

```{note}
`cosineSimilarity()` returns `0.0f` for zero vectors and handles
floating-point rounding at the [-1, 1] boundaries.
```

## Semantic search example

```cpp
// Build a knowledge base
std::vector<std::pair<String, yup::EmbeddingModel::Embedding>> knowledgeBase;

auto addDocument = [&](const String& content)
{
knowledgeBase.push_back ({ content, embeddingModel.embed (content) });
};

addDocument ("C++ is a statically typed, compiled language.");
addDocument ("Python is dynamically typed and interpreted.");
addDocument ("Rust guarantees memory safety without a garbage collector.");

// Search
auto query = embeddingModel.embed ("Which language is compiled?");
std::sort (knowledgeBase.begin(), knowledgeBase.end(),
[&](const auto& a, const auto& b)
{
return yup::EmbeddingModel::cosineSimilarity (query, a.second)
> yup::EmbeddingModel::cosineSimilarity (query, b.second);
});

// Top match: "C++ is a statically typed, compiled language."
DBG (knowledgeBase.front().first);
```
78 changes: 78 additions & 0 deletions docs/ai/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# AI

LLM chat-completion clients, text embeddings, and MCP (Model Context Protocol)
server and client — all built on `yup_core` and `yup_events`.

**Module:** `yup_ai`.

## In this area

- [LLM clients](llm.md) — chat completions, streaming, tools, structured output,
and provider setup (OpenAI, Anthropic, Gemini, Ollama, DeepSeek, llama-server).
- [MCP](mcp.md) — Model Context Protocol server and client for tool/resource
bridging over JSON-RPC transports.
- [Embeddings](embedding.md) — text embeddings and cosine similarity for
semantic search and retrieval.

## Key building blocks

The `yup_ai` module provides:

- **`LLMClient`** — abstract base for every chat-completion provider with
`complete()`, `completeStreaming()`, `chat()`, and `runToolLoop()`.
- **`LLMClientFactory`** — a single `create()` call that picks the correct
provider from `LLMClient::Options::provider`, plus convenience factories
(`openAIChat()`, `anthropic()`, `gemini()`, etc.).
- **`LLMMessage` / `LLMResponse`** — message and response types compatible
with OpenAI's ChatML format.
- **`LLMTool` / `LLMToolRegistry`** — define callable functions with JSON
Schema parameters and register/dispatch them thread-safely.
- **`LLMSchema`** — fluent builder for JSON Schema objects used in
structured-output requests.
- **`EmbeddingModel`** — embed text via any OpenAI-compatible embeddings
endpoint.
- **`MCPClient` / `MCPServer`** — JSON-RPC 2.0 bridge for Model Context
Protocol: list and call tools, register resources.
- **`MCPTransport`** — abstract transport; implementations for stdio, HTTP/SSE,
and in-process queues.

## Quick start

```cpp
#include <yup_ai/yup_ai.h>

// Create an OpenAI Chat client
auto client = yup::LLMClientFactory::openAIChat (
"gpt-5",
"https://api.openai.com/v1",
"sk-..."
);

// One-shot chat
auto response = client->chat ("What is the capital of France?");
if (! response.failed())
DBG (response.choices.front().message.content); // "Paris"

// Structured output with JSON Schema
yup::LLMClient::Request request;
request.messages.push_back (yup::LLMMessage::user ("Extract the title and year."));
request.schema = yup::LLMSchema::object ({
{ "title", yup::LLMSchema::string() },
{ "year", yup::LLMSchema::integer() },
});
auto structured = client->complete (request);
```

## Related areas

- [Scripting](../scripting/index.md) — Python bindings are available for `yup_ai`
when both modules are linked.

```{toctree}
:hidden:
:maxdepth: 2

llm
mcp
embedding
```
Loading
Loading