From edf6f7cd4dbc3a360c1fc0e1db911e7effe30513 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 27 May 2026 21:41:06 +0300 Subject: [PATCH 1/5] Document AI/LLM, ChatView, speech, and ML Kit cn1libs from PR #5035 PR #5035 introduced com.codename1.ai (LlmClient, ChatRequest, streaming SSE, tool calls, embeddings, image generation, conversation persistence, retries, simulator Ollama redirect), the com.codename1.components.ChatView component, com.codename1.media.SpeechRecognizer / TextToSpeech, the non-prompting SecureStorage overloads, and the cn1-ai-mlkit-{barcode, docscan,face} cn1libs. The PR shipped the framework code but left both the developer guide and the initializr authoring skill silent on every new API. Adds a new "AI, Chat UI, and Speech" chapter to the developer guide: provider factories, streaming, tool calling, structured output, multi- modal messages, embeddings, image generation, conversation store, prompt templates, retry policy, simulator redirect, SecureStorage key handling, ChatView wiring and theming UIIDs, SpeechRecognizer / TextToSpeech, the three ML Kit cn1libs, and a capture-photo -> describe -> speak example. Includes an SVG mockup of the ChatView surface. Adds references/ai-and-speech.md to the bundled initializr authoring skill, indexed from SKILL.md and exercised from GeneratorModelMatrixTest, so Claude Code (and any agent that respects the skills convention) inside a generated project picks up the new APIs automatically. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/developer-guide/Ai-And-Speech.asciidoc | 609 ++++++++++++++++++ docs/developer-guide/developer-guide.asciidoc | 2 + docs/developer-guide/img/chat-view-mockup.svg | 67 ++ docs/developer-guide/languagetool-accept.txt | 12 + .../common/src/main/resources/skill/SKILL.md | 5 + .../skill/references/ai-and-speech.md | 414 ++++++++++++ .../model/GeneratorModelMatrixTest.java | 1 + 7 files changed, 1110 insertions(+) create mode 100644 docs/developer-guide/Ai-And-Speech.asciidoc create mode 100644 docs/developer-guide/img/chat-view-mockup.svg create mode 100644 scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md diff --git a/docs/developer-guide/Ai-And-Speech.asciidoc b/docs/developer-guide/Ai-And-Speech.asciidoc new file mode 100644 index 0000000000..fc4e48b5ef --- /dev/null +++ b/docs/developer-guide/Ai-And-Speech.asciidoc @@ -0,0 +1,609 @@ +== AI, Chat UI, and Speech + +[[ai-and-speech-section,AI And Speech Section]] +Codename One ships a portable LLM client, a streaming chat component, +speech-to-text and text-to-speech APIs, and a family of cn1lib bridges +that wire ML Kit into iOS and Android builds. All the public surface +lives next to the rest of the framework, so the simulator, the cloud +build, and your CI pipeline route everything through the same code +paths. + +This chapter introduces every piece in turn: + +* `com.codename1.ai` -- provider-agnostic chat, embeddings, tool calls, + and image generation. +* `com.codename1.components.ChatView` -- a theme-aware chat surface that + streams tokens in place. +* `com.codename1.media.SpeechRecognizer` and + `com.codename1.media.TextToSpeech` -- on-device speech-to-text and + speech synthesis. +* `com.codename1.security.SecureStorage` non-prompting overloads -- + silent reads for secrets the network layer needs on every call, such + as LLM API keys. +* The `cn1-ai-mlkit-*` cn1libs -- ML Kit barcode, document, and face + detection bridges with the native build-time scanner already wired up. + +NOTE: Each async call in this chapter returns an +`com.codename1.util.AsyncResource`. Use `.ready(...)` for the success +path, `.except(...)` for errors, and `.cancel()` to abort an in-flight +request. The streaming chat call also dispatches deltas through a +`StreamingListener`; both listener callbacks and `ready` callbacks fire +on the EDT. + +=== The `com.codename1.ai` package + +The `LlmClient` class is the single entry point. Static factories return +a configured client for each supported provider. Every client speaks the +same `ChatRequest` and `ChatResponse` value types, so the call site does +not change when you swap providers: + +[source,java] +---- +import com.codename1.ai.*; + +// OpenAI (also drives Ollama, vLLM, llama.cpp, and other +// OpenAI-compatible endpoints). +LlmClient openai = LlmClient.openai(apiKey); + +// Local Ollama on the default port (http://localhost:11434). +LlmClient ollama = LlmClient.ollama("llama3.2"); + +// Any OpenAI-compatible endpoint (Together, Groq, Fireworks, vLLM, ...). +LlmClient together = LlmClient.localOpenAiCompatible( + "https://api.together.xyz/v1", + apiKey, + "meta-llama/Llama-3.3-70B-Instruct-Turbo"); + +// Scaffolded; native wire format pending. Today these throw a clear +// error pointing callers at the OpenAI-compatible shim. +LlmClient anthropic = LlmClient.anthropic(apiKey); +LlmClient gemini = LlmClient.gemini(apiKey); +---- + +The OpenAI client is the fully implemented native client. It also drives +Ollama, vLLM, and `llama.cpp` because the wire format matches. The +Anthropic and Gemini factories compile and register, but throw an +`LlmException` until their native clients land, so route through +`localOpenAiCompatible` if you need them today. + +==== A first chat + +`ChatRequest` is a builder. The bare minimum is a model identifier and +one user message: + +[source,java] +---- +ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini") + .addMessage(ChatMessage.system("Reply in haiku.")) + .addMessage(ChatMessage.user("Describe a Codename One app.")) + .temperature(0.7f) + .maxTokens(200) + .build(); + +openai.chat(req).ready(resp -> { + Dialog.show("Reply", resp.getText(), "OK", null); +}).except(err -> { + Log.e(err); +}); +---- + +`ChatResponse.getText()` concatenates every text part of the assistant +message. `getFinishReason()` returns one of `"stop"`, `"length"`, +`"tool_calls"`, `"content_filter"`, or `"error"`. `getUsage()` returns +prompt, completion, and total token counts when the provider reports +them; the fields return -1 when the provider omits them. + +==== Streaming tokens + +`chatStream(...)` opens an SSE connection and dispatches deltas through +a `StreamingListener` on the EDT. The returned `AsyncResource` resolves +to the final aggregated `ChatResponse` when the stream completes; +calling `cancel()` on the resource closes the underlying socket: + +[source,java] +---- +StringBuilder buffer = new StringBuilder(); + +openai.chatStream(req, new StreamingListener.Adapter() { + @Override + public void onContentDelta(String delta) { + buffer.append(delta); + chatView.appendToLastMessage(delta); + } + + @Override + public void onError(Throwable t) { + Log.e(t); + } +}).ready(resp -> { + Log.p("Total tokens: " + resp.getUsage().getTotalTokens()); +}); +---- + +The decoder reassembles fragmented SSE deltas before invoking the +listener, so `onContentDelta` always receives a complete token chunk. +Tool-call fragments are reassembled the same way and surfaced through +`onToolCallDelta(index, id, name, argumentsFragment)`; `name` is non-null +on the first fragment and `id` is present on the first call. + +==== Tool calling + +`Tool` describes a callable function with a JSON schema. Pass a +`ToolHandler` to make it executable; the model can then call the tool +and the handler returns the JSON result that gets fed back into the +conversation: + +[source,java] +---- +Tool weather = new Tool( + "get_weather", + "Return the current weather for a city.", + "{\"type\":\"object\",\"properties\":{" + + "\"city\":{\"type\":\"string\"}}," + + "\"required\":[\"city\"]}", + argumentsJson -> { + Map args = JsonHelper.parseObject(argumentsJson); + return "{\"tempC\": 22, \"city\": \"" + args.get("city") + "\"}"; + }); + +ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini") + .addMessage(ChatMessage.user("What is the weather in Tel Aviv?")) + .tools(Collections.singletonList(weather)) + .toolChoice(ToolChoice.AUTO) + .build(); + +openai.chat(req).ready(resp -> { + for (ToolCall call : resp.getToolCalls()) { + String result = call.execute(Collections.singletonList(weather)); + // Feed the tool result back as a new turn and call chat() again. + } +}); +---- + +`ToolChoice` covers the four standard modes: + +* `ToolChoice.AUTO` -- the model picks at will (default). +* `ToolChoice.NONE` -- the model must not call any tool. +* `ToolChoice.REQUIRED` -- the model must call exactly one tool. +* `ToolChoice.named("get_weather")` -- force a specific tool. + +==== Structured output + +Constrain a response to JSON with `ResponseFormat.JSON_OBJECT`. The +client adds the provider-specific flag, and `ChatResponse.getText()` +returns a string the runtime can hand to `JSONParser`: + +[source,java] +---- +ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini") + .responseFormat(ResponseFormat.JSON_OBJECT) + .addMessage(ChatMessage.system( + "Return a JSON object with keys city and population.")) + .addMessage(ChatMessage.user("Tel Aviv")) + .build(); +---- + +==== Multi-modal messages + +Attach an image to a user message by adding an `ImagePart`. The part +accepts either inline bytes plus a MIME type or a remote HTTPS URL: + +[source,java] +---- +ImagePart photo = new ImagePart(receiptBytes, "image/jpeg"); +ChatMessage msg = ChatMessage.userWithImage( + "Extract the line items from this receipt.", photo); + +ChatRequest req = ChatRequest.builder() + .model("gpt-4o") + .addMessage(msg) + .build(); +---- + +==== Embeddings + +`embed(EmbeddingRequest)` returns one `Embedding` vector per input. Use +the vectors for semantic search, deduplication, or downstream +clustering: + +[source,java] +---- +EmbeddingRequest req = EmbeddingRequest.builder() + .model("text-embedding-3-small") + .addInput("a cat sat on the mat") + .addInput("a feline rested on the rug") + .build(); + +openai.embed(req).ready(resp -> { + float[] v0 = resp.getData().get(0).getVector(); + float[] v1 = resp.getData().get(1).getVector(); + // Compute cosine similarity, persist to Storage, etc. +}); +---- + +==== Image generation + +`ImageGenerator` mirrors the LLM client shape. The OpenAI factory drives +DALL-E 3 today; the on-device factory routes through the optional +`cn1-ai-stablediffusion` cn1lib (when present in the consumer project): + +[source,java] +---- +ImageGenerator gen = ImageGenerator.openai(apiKey); +GenerateImageRequest req = new GenerateImageRequest( + "A pastel watercolor of a Tel Aviv beach at sunset"); +req.setSize("1024x1024"); +req.setQuality("hd"); + +gen.generate(req).ready(image -> { + Label preview = new Label(image); + Display.getInstance().getCurrent().add(preview).revalidate(); +}); +---- + +DALL-E 3 supports `count = 1` only. Larger batches require a different +underlying model; pass it via `setModel(...)`. + +==== Conversation persistence + +`ConversationStore` wraps `Storage` to serialize a list of +`ChatMessage` values to JSON under a named key: + +[source,java] +---- +ConversationStore store = new ConversationStore("chat-history"); +List history = store.load(); // empty list on first call + +history.add(ChatMessage.user("Hello")); +ChatResponse resp = openai.chat( + ChatRequest.builder().model("gpt-4o-mini").messages(history).build()) + .get(); // blocking helper, EDT-safe +history.add(resp.getAssistantMessage()); +store.save(history); +---- + +`Tokenizer.estimateMessages(history)` returns a rough token count so you +can trim the oldest turns before the conversation outgrows the model's +context window. + +==== Prompt templates + +`PromptTemplate` does simple `{placeholder}` substitution. Unknown +placeholders pass through unchanged so partially-filled templates are +safe to log: + +[source,java] +---- +PromptTemplate t = PromptTemplate.of( + "Translate the following from {source} to {target}: {text}"); +t.put("source", "English"); +t.put("target", "French"); +t.put("text", "good morning"); + +ChatMessage user = t.asUser(); // wraps the rendered string +---- + +==== Retry policy + +`RetryPolicy.exponentialBackoff()` returns a sensible default (four +attempts, 500ms initial delay, 30s cap, jitter). Wrap any chat call in a +retry loop yourself, or attach the policy to a higher-level wrapper. +`LlmException.getRetryAfterSeconds()` returns the provider's +`Retry-After` header value when present, or -1, so the policy can honor +rate-limit hints. + +==== Simulator redirect for offline development + +The JavaSE simulator pings `localhost:11434` at startup. When Ollama is +running, the simulator exports the flag `cn1.ai.ollamaDetected=true`. A +second system property, `cn1.ai.simulatorRedirect`, controls whether the +simulator transparently routes `LlmClient.openai(...)` calls to the +local Ollama endpoint instead: + +[cols="1,3", options="header"] +|=== +| Value | Behavior + +| `disabled` | The default on a device. Calls go to the configured +provider, even in the simulator. +| `auto` | The default in the simulator. If Ollama is reachable, route +OpenAI calls through it. Otherwise behave like `disabled`. +| `ollama` | Force redirect to Ollama. Use this in offline-only +development environments. +|=== + +The redirect target is also configurable: `cn1.ai.ollamaUrl` defaults to +`http://localhost:11434/v1`, and `cn1.ai.ollamaModel` defaults to +`llama3.2`. Unchanged production code therefore runs offline against a +local model without any conditional wiring at the call site. + +==== Storing the API key + +Production builds need somewhere to keep the provider API key. The +non-prompting `SecureStorage` overloads (added in the same release as +the AI surface) are designed for this: a single string argument, no +biometric prompt, return `null` or `false` on platforms that lack +support: + +[source,java] +---- +SecureStorage store = SecureStorage.getInstance(); +store.set("openai.key", apiKey); // returns false when unsupported + +String key = store.get("openai.key"); // returns null when absent +LlmClient client = LlmClient.openai(key); +---- + +The biometric-gated overloads (`get(reason, account)`, +`set(reason, account, value)`, `remove(reason, account)`) remain the +right choice for credentials a human user authenticates against. Use the +non-prompting overloads only for secrets the network layer reads on +every request, where a biometric prompt would be unusable. + +=== The `ChatView` component + +`ChatView` is a scrollable, theme-aware chat surface that handles the +list of bubbles, the streaming append, the typing indicator, and the +input strip in one component. Drop it into a `Form` with a single +`BorderLayout.CENTER`: + +.ChatView mockup +image::img/chat-view-mockup.svg[ChatView mockup, 360, 640] + +The component exposes thread-safe mutators, so background callbacks +from `chatStream(...)` can mutate the view directly: + +[source,java] +---- +Form chat = new Form("Assistant", new BorderLayout()); +ChatView view = new ChatView(); +chat.add(BorderLayout.CENTER, view); + +view.addMessage(ChatMessage.assistant("How can I help?")); + +view.setOnSend(e -> { + String text = view.getInput().getText(); + view.getInput().clear(); + view.addMessage(ChatMessage.user(text)); + view.setTypingIndicatorVisible(true); + + ChatBubble streaming = view.beginAssistantStream(); + ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini") + .messages(view.getHistory()) + .build(); + + LlmClient.openai(apiKey).chatStream(req, new StreamingListener.Adapter() { + @Override public void onContentDelta(String d) { + view.appendToLastMessage(d); + } + }).ready(resp -> view.setTypingIndicatorVisible(false)); +}); +chat.show(); +---- + +`ChatBubble` and `ChatInput` are public, so subclass them for custom +rendering. Override `ChatView.createBubble(message)` to swap in a +custom subclass when the view builds the message list. + +==== Theming + +`ChatView` exposes the following UIIDs out of the box: + +[cols="1,2", options="header"] +|=== +| UIID | Applies to +| `ChatView` | The outer container. +| `ChatViewMessages` | The scrollable message column. +| `ChatBubbleUser` | The container for a user message. +| `ChatBubbleAssistant` | The container for an assistant message. +| `ChatBubbleSystem` | The container for a system message. +| `ChatBubbleText` | The inner `TextArea` of every bubble. +| `ChatTypingIndicator` | The animated typing dots. +| `ChatInput` | The input strip. +| `ChatInputField` | The text field inside the strip. +| `ChatSendButton` | The send button. +| `ChatAttachButton` | The attach button. +| `ChatVoiceButton` | The voice button. +|=== + +Style them from `theme.css` to align with the rest of the app. Hide the +voice or attach button by leaving its listener unset; the corresponding +`Button` instance is hidden when no `ActionListener` is registered. + +==== One-call binding to an LLM + +`LlmChatBinding.bind(view, client, baseRequest)` wires the input bar +directly to `chatStream(...)`. Every send replays the conversation +history, dispatches the response into the view, and updates the typing +indicator. Use it for prototypes, or as a reference implementation when +you need a custom send pipeline: + +[source,java] +---- +LlmChatBinding.bind(view, + LlmClient.openai(apiKey), + ChatRequest.builder().model("gpt-4o-mini").build()); +---- + +=== Speech recognition and TextToSpeech + +The new media APIs route through `Display` and into the implementation +hooks on `CodenameOneImplementation`, so the call surface is identical +on every platform. The default implementation returns `false` from +`isSupported()` and is a no-op for `recognize`/`speak`. + +==== `SpeechRecognizer` + +[source,java] +---- +import com.codename1.media.*; + +if (!SpeechRecognizer.isSupported()) { + Dialog.show("Unavailable", "Speech is not supported on this device.", + "OK", null); + return; +} + +RecognitionOptions opts = new RecognitionOptions() + .setLanguageTag("en-US") + .setPartialResults(true) + .setContinuous(false) + .setMaxResults(3); + +SpeechRecognizer.recognize(opts, new RecognitionCallback.Adapter() { + @Override public void onPartialResult(String transcript) { + chatView.getInput().setText(transcript); + } + @Override public void onResult(String transcript, float confidence, + String[] alternatives) { + chatView.addMessage(ChatMessage.user(transcript)); + } +}); +---- + +iOS uses `SFSpeechRecognizer`, Android uses `android.speech.SpeechRecognizer`, +and the JavaSE simulator stays unsupported unless the optional +`cn1-ai-whisper` cn1lib is on the classpath. Call `stop()` to end an +active session early; partial results stop firing immediately. + +==== `TextToSpeech` + +[source,java] +---- +if (TextToSpeech.isSupported()) { + TtsOptions opts = new TtsOptions() + .setLanguageTag("fr-FR") + .setRate(1.0f); + TextToSpeech.speak("Bonjour", opts); +} +---- + +iOS uses `AVSpeechSynthesizer`, Android uses +`android.speech.tts.TextToSpeech`, and the JavaSE simulator falls back +to `say` on macOS, `espeak` on Linux, and SAPI on Windows. +`getAvailableVoices()` returns the platform-specific voice identifiers +when the OS exposes them; `setVoiceId(...)` accepts any of those +strings, or `null` to use the default voice for the configured +language. + +=== ML Kit cn1libs + +Three cn1libs ship with the framework today, each backed by ML Kit on +both platforms. The build-time scanner picks up the `com.codename1.ai.*` +class references in your code and injects the matching Pods, Swift +Packages, Gradle dependencies, `Info.plist` usage strings, and Android +permissions; you don't edit build hints by hand. + +[cols="1,1,2", options="header"] +|=== +| cn1lib | Public API | Provides + +| `cn1-ai-mlkit-barcode` | `BarcodeScanner.scan(byte[])` | Decode QR, +EAN, Code 128, and the other ML Kit-supported barcode formats from an +image. +| `cn1-ai-mlkit-docscan` | `DocumentScanner.scanToFile(byte[])` | +Capture and crop document photos. On iOS this routes through `VisionKit` +and on Android through the Google Play Services document scanner. +| `cn1-ai-mlkit-face` | `FaceDetector.detect(byte[])` | Detect faces and +return packed `int[]` bounding rectangles (four ints per face: x, y, +width, height). +|=== + +==== Adding a cn1lib + +Add the dependency to `common/pom.xml` with `pom` so Maven +pulls in the per-platform classifier jars: + +[source,xml] +---- + + com.codenameone + cn1-ai-mlkit-barcode-lib + ${cn1.version} + pom + +---- + +==== Example: Scanning a barcode + +[source,java] +---- +import com.codename1.ai.mlkit.barcode.BarcodeScanner; + +Capture.capturePhoto(new ActionListener() { + @Override public void actionPerformed(ActionEvent evt) { + String path = (String) evt.getSource(); + try (InputStream in = FileSystemStorage.getInstance().openInputStream(path)) { + byte[] bytes = Util.readInputStream(in); + BarcodeScanner.scan(bytes).ready(values -> { + for (String v : values) { + Log.p("Detected: " + v); + } + }); + } catch (IOException ex) { + Log.e(ex); + } + } +}); +---- + +==== Example: Face detection + +[source,java] +---- +import com.codename1.ai.mlkit.face.FaceDetector; + +FaceDetector.detect(jpegBytes).ready(rects -> { + for (int i = 0; i < rects.length; i += 4) { + Log.p("Face at (" + rects[i] + "," + rects[i + 1] + + ") size " + rects[i + 2] + "x" + rects[i + 3]); + } +}); +---- + +NOTE: ML Kit barcode and face detection ship as small Pods or Gradle +dependencies that add a few megabytes to the binary. The document +scanner depends on Google Play Services on Android; on devices without +Play Services the call returns an error through `AsyncResource.except`. + +==== Putting it all together + +The pieces compose well. The following loop captures a photo, +extracts text with the multi-modal `gpt-4o` model, speaks the result, +and streams the same text into a `ChatView`: + +[source,java] +---- +Capture.capturePhoto(evt -> { + String path = (String) evt.getSource(); + byte[] bytes = readAllBytes(path); + ImagePart img = new ImagePart(bytes, "image/jpeg"); + + ChatRequest req = ChatRequest.builder() + .model("gpt-4o") + .addMessage(ChatMessage.userWithImage( + "Describe the photo in one sentence.", img)) + .build(); + + chatView.addMessage(req.getMessages().get(0)); + ChatBubble streaming = chatView.beginAssistantStream(); + StringBuilder full = new StringBuilder(); + + LlmClient.openai(apiKey).chatStream(req, new StreamingListener.Adapter() { + @Override public void onContentDelta(String d) { + full.append(d); + chatView.appendToLastMessage(d); + } + }).ready(resp -> { + TextToSpeech.speak(full.toString()); + }); +}); +---- + +The same code runs unchanged in the simulator when Ollama is detected, +on Android with the multi-modal native client, and on iOS through the +cloud build pipeline. diff --git a/docs/developer-guide/developer-guide.asciidoc b/docs/developer-guide/developer-guide.asciidoc index 22a3fdab94..b6d1faf3a5 100644 --- a/docs/developer-guide/developer-guide.asciidoc +++ b/docs/developer-guide/developer-guide.asciidoc @@ -96,6 +96,8 @@ include::Annotation-Component-Binding.asciidoc[] include::Annotation-SQLite-ORM.asciidoc[] +include::Ai-And-Speech.asciidoc[] + include::Near-Field-Communication.asciidoc[] include::Network-Connectivity.asciidoc[] diff --git a/docs/developer-guide/img/chat-view-mockup.svg b/docs/developer-guide/img/chat-view-mockup.svg new file mode 100644 index 0000000000..1d61a2ef4e --- /dev/null +++ b/docs/developer-guide/img/chat-view-mockup.svg @@ -0,0 +1,67 @@ + + + ChatView mockup + Mockup of the com.codename1.components.ChatView component showing a system bubble, two user bubbles, two assistant bubbles, a typing indicator, and the ChatInput strip with attach, text field, voice, and send buttons. + + + + + + + Assistant + 14:32 + + + + + + + Session started + + + + Translate "good morning" to French + + + + "Bonjour" (literally "good day"). + + + + Now read it out and + show me an image. + + + + Sure. Streaming an image now + and speaking the translation + via TextToSpeech.speak(...). + + + + + + + + + + + + + + + + + + + + Message + + + + + + + + + + diff --git a/docs/developer-guide/languagetool-accept.txt b/docs/developer-guide/languagetool-accept.txt index fb2bb66105..b221666bbc 100644 --- a/docs/developer-guide/languagetool-accept.txt +++ b/docs/developer-guide/languagetool-accept.txt @@ -530,3 +530,15 @@ webauthn # dictionary. [Dd]ao [Dd]aos + +# ----------------------------------------------------------------------------- +# AI / LLM provider, product, and model names used in the AI, Chat UI, and +# Speech chapter. LanguageTool's en_US dictionary either doesn't recognise +# these or mis-spells them. +# ----------------------------------------------------------------------------- +Ollama +[Vv]LLM +[Ll]lama +[Aa]nthropic +SAPI +espeak diff --git a/scripts/initializr/common/src/main/resources/skill/SKILL.md b/scripts/initializr/common/src/main/resources/skill/SKILL.md index d6f730b088..447d5ad114 100644 --- a/scripts/initializr/common/src/main/resources/skill/SKILL.md +++ b/scripts/initializr/common/src/main/resources/skill/SKILL.md @@ -33,6 +33,7 @@ This skill teaches you how to write code for a Codename One (CN1) cross-platform - `references/mobile-adaptability.md` — Density-independent units (mm), `convertToPixels`, `LayeredLayout` for responsive design, `Display.isTablet()`, font scaling. - `references/native-interfaces.md` — Authoring native interfaces for iOS/Android/JavaScript/Desktop with `cn1:generate-native-interfaces` and platform callbacks. - `references/cn1libs.md` — Creating, packaging, and consuming Codename One libraries (Maven and legacy `.cn1lib`). +- `references/ai-and-speech.md` — LLM client (`com.codename1.ai`), `ChatView`, `SpeechRecognizer`, `TextToSpeech`, non-prompting `SecureStorage` overloads, the ML Kit cn1libs, and the simulator's offline Ollama redirect. Read this when the user asks for chat, voice, embeddings, image generation, barcode/document/face detection, or wants to store an LLM API key. - `references/snapshot-builds.md` — Edge case: compiling against a Codename One SNAPSHOT from git. - `references/debugging.md` — `jdb`-attach workflow for an agent: start the simulator paused, set breakpoints, dump locals, drive the session non-interactively from a script. - `tools/` — runnable Java 17 single-file utilities. `tools/IsApiSupported.java` answers "is this `java.*` class in the CN1 subset?"; `tools/IsCssValid.java` answers "does this `theme.css` compile?". Run with `java tools/.java `. @@ -292,6 +293,10 @@ If you cannot run the simulator (e.g. headless environment), **say so explicitly | "Why does the compliance check fail" / Java/IO/networking | `references/java-api-subset.md` | | "I need to call a native iOS/Android/JS/desktop API" | `references/native-interfaces.md` | | "How do I create / consume a cn1lib" | `references/cn1libs.md` | +| "Add a chatbot" / "Integrate OpenAI/Ollama/Anthropic" / "Stream LLM tokens" / "Generate an image" / "Embed text" | `references/ai-and-speech.md` | +| "Read voice input" / "Speak text aloud" / "Add a voice button to my chat" | `references/ai-and-speech.md` | +| "Scan a barcode" / "Detect a face" / "Crop a document photo" via ML Kit | `references/ai-and-speech.md` | +| "Store an LLM API key" / non-prompting SecureStorage | `references/ai-and-speech.md` | | "Build against a Codename One SNAPSHOT from git" | `references/snapshot-builds.md` | | "Debug a faulty screen — attach `jdb` to the simulator" | `references/debugging.md` | | Quick yes/no check: "is this `java.*` class supported", "does my `theme.css` compile" | `tools/` directory — `java tools/IsApiSupported.java ` / `java tools/IsCssValid.java ` | diff --git a/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md b/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md new file mode 100644 index 0000000000..cf6fbdb27a --- /dev/null +++ b/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md @@ -0,0 +1,414 @@ +# AI, Chat UI, and Speech Reference + +Codename One ships a portable LLM client, a streaming chat component, speech recognition, text-to-speech, and ML Kit cn1lib bridges. All of it sits in the cross-platform `common/` module — the same call site runs on iOS, Android, JavaSE, and (where the backend supports it) JavaScript. + +**Read this reference when** the user asks to integrate an LLM, build a chat UI, voice input, voice output, image generation, embeddings, on-device barcode/face/document scanning, or wants to store an API key. + +## Core APIs at a glance + +| Concern | Class | Module | +| --- | --- | --- | +| Chat / embeddings / image generation | `com.codename1.ai.LlmClient` | core (built-in) | +| Streaming-aware chat UI | `com.codename1.components.ChatView` | core (built-in) | +| Speech-to-text | `com.codename1.media.SpeechRecognizer` | core (built-in) | +| Text-to-speech | `com.codename1.media.TextToSpeech` | core (built-in) | +| Silent secret storage (LLM API keys, etc.) | Single-arg overloads on `com.codename1.security.SecureStorage` | core (built-in) | +| Barcode scanning | `com.codename1.ai.mlkit.barcode.BarcodeScanner` | cn1lib `cn1-ai-mlkit-barcode` | +| Document scanning | `com.codename1.ai.mlkit.docscan.DocumentScanner` | cn1lib `cn1-ai-mlkit-docscan` | +| Face detection | `com.codename1.ai.mlkit.face.FaceDetector` | cn1lib `cn1-ai-mlkit-face` | + +The build-time scanner in the Codename One Maven plugin (`AiDependencyTable`) picks up references to any `com.codename1.ai.*` or `com.codename1.media.{Speech,Tts}*` class and automatically wires Pods (iOS), Swift Packages (iOS SPM), Gradle dependencies (Android), `Info.plist` usage strings, and Android permissions. You don't edit `codenameone_settings.properties` build hints for these classes. + +## LlmClient — chat, embeddings, image generation + +```java +import com.codename1.ai.*; + +// OpenAI (the fully implemented native client). Also drives Ollama, +// vLLM, llama.cpp, Together, Groq, Fireworks etc. via shared wire format. +LlmClient client = LlmClient.openai(apiKey); + +// Local Ollama on http://localhost:11434 +LlmClient local = LlmClient.ollama("llama3.2"); + +// Any OpenAI-compatible endpoint +LlmClient together = LlmClient.localOpenAiCompatible( + "https://api.together.xyz/v1", apiKey, "meta-llama/Llama-3.3-70B-Instruct-Turbo"); + +// Scaffolds today: throw a clear error until native clients land. +// Route via localOpenAiCompatible() instead. +LlmClient.anthropic(apiKey); +LlmClient.gemini(apiKey); +``` + +`ChatRequest` is a fluent builder. `chat()` returns the response, `chatStream()` streams deltas: + +```java +ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini") + .addMessage(ChatMessage.system("Reply in haiku.")) + .addMessage(ChatMessage.user("Describe a Codename One app.")) + .temperature(0.7f) + .maxTokens(200) + .build(); + +client.chat(req).ready(resp -> Log.p(resp.getText())); + +// Streaming +client.chatStream(req, new StreamingListener.Adapter() { + @Override public void onContentDelta(String delta) { + chatView.appendToLastMessage(delta); + } +}).ready(resp -> Log.p("usage=" + resp.getUsage().getTotalTokens())); +``` + +All callbacks fire on the EDT. `AsyncResource.cancel()` closes the socket on a streaming call. + +### Roles, message parts, multi-modal + +```java +ChatMessage.system("You are a tour guide."); +ChatMessage.user("Describe Paris."); +ChatMessage.assistant("Paris is…"); + +// Multi-modal: attach an image +ImagePart photo = new ImagePart(jpegBytes, "image/jpeg"); // inline +ImagePart byUrl = new ImagePart("https://example.com/x.png"); // remote +ChatMessage withImg = ChatMessage.userWithImage("Describe the photo.", photo); +``` + +### Tool calling + +```java +Tool weather = new Tool( + "get_weather", + "Return the current weather for a city.", + "{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}}," + + "\"required\":[\"city\"]}", + argsJson -> "{\"tempC\":22}"); // ToolHandler + +ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini") + .addMessage(ChatMessage.user("Weather in Paris?")) + .tools(Collections.singletonList(weather)) + .toolChoice(ToolChoice.AUTO) // .NONE | .REQUIRED | .named("x") + .build(); + +client.chat(req).ready(resp -> { + for (ToolCall call : resp.getToolCalls()) { + String result = call.execute(Collections.singletonList(weather)); + // Feed result back as ChatMessage.toolResult(call.getId(), result) + // and call chat() again. + } +}); +``` + +### Structured output + +```java +ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini") + .responseFormat(ResponseFormat.JSON_OBJECT) + .addMessage(ChatMessage.system("Return JSON with keys city, population.")) + .addMessage(ChatMessage.user("Tel Aviv")) + .build(); +``` + +### Embeddings + +```java +EmbeddingRequest req = EmbeddingRequest.builder() + .model("text-embedding-3-small") + .addInput("a cat sat on the mat") + .build(); + +client.embed(req).ready(resp -> { + float[] v = resp.getData().get(0).getVector(); + // store, compare cosine similarity, etc. +}); +``` + +### Image generation + +```java +ImageGenerator gen = ImageGenerator.openai(apiKey); // DALL-E 3 +GenerateImageRequest req = new GenerateImageRequest("Watercolor of a beach at sunset"); +req.setSize("1024x1024"); +gen.generate(req).ready(image -> form.add(new Label(image)).revalidate()); +``` + +`ImageGenerator.onDevice()` routes to the optional `cn1-ai-stablediffusion` cn1lib for on-device Stable Diffusion. That cn1lib carries multi-GB native blobs; the build server flips `cn1.ai.requiresBigUpload` and asks you to build locally if your project bundles it. + +### Conversation persistence, retries, prompts, tokens + +| Class | Purpose | +| --- | --- | +| `ConversationStore(key)` | JSON-serialize a `List` to/from `Storage` | +| `PromptTemplate.of("Translate {text} to {lang}")` | Trivial `{placeholder}` substitution | +| `Tokenizer.estimate(text)` / `Tokenizer.estimateMessages(history)` | Approximate token count | +| `RetryPolicy.exponentialBackoff()` | 4 attempts, 500ms→30s, jitter; honors `Retry-After` from `LlmException.getRetryAfterSeconds()` | +| `SafetyFilter.check(messages)` | Returns `null` to allow, non-null reason to block; pre-flight gate | + +### LlmException + +Single checked exception extending `IOException`. Use `LlmException.getType()` to switch on: + +``` +AUTH, RATE_LIMIT, INVALID_REQUEST, CONTEXT_LENGTH, +MODEL_OVERLOADED, SERVER, NETWORK, UNKNOWN +``` + +`getHttpStatus()`, `getProviderErrorCode()`, `getRawBody()`, `getRetryAfterSeconds()` are also exposed for logging and retry decisions. + +### Simulator redirect (offline Ollama) + +The JavaSE simulator probes `localhost:11434` at startup. Two system properties drive the redirect: + +| Property | Default | Effect | +| --- | --- | --- | +| `cn1.ai.simulatorRedirect` | `auto` in simulator, `disabled` on device | `auto` redirects OpenAI calls to local Ollama when Ollama is reachable. `ollama` forces redirect. `disabled` always hits the configured provider. | +| `cn1.ai.ollamaUrl` | `http://localhost:11434/v1` | Override the Ollama endpoint URL | +| `cn1.ai.ollamaModel` | `llama3.2` | Override the local model name | +| `cn1.ai.ollamaDetected` | (read-only) | `true` if the startup probe found Ollama | + +Production code calling `LlmClient.openai(...)` runs unchanged in the simulator against the local model. No API charges, no network round-trip. + +## ChatView — streaming chat surface + +```java +import com.codename1.components.*; + +Form chat = new Form("Assistant", new BorderLayout()); +ChatView view = new ChatView(); +chat.add(BorderLayout.CENTER, view); + +view.addMessage(ChatMessage.assistant("How can I help?")); + +view.setOnSend(e -> { + String text = view.getInput().getText(); + view.getInput().clear(); + view.addMessage(ChatMessage.user(text)); + view.setTypingIndicatorVisible(true); + ChatBubble streaming = view.beginAssistantStream(); + + ChatRequest req = ChatRequest.builder() + .model("gpt-4o-mini").messages(view.getHistory()).build(); + + LlmClient.openai(apiKey).chatStream(req, new StreamingListener.Adapter() { + @Override public void onContentDelta(String d) { + view.appendToLastMessage(d); + } + }).ready(resp -> view.setTypingIndicatorVisible(false)); +}); +chat.show(); +``` + +The component is thread-safe: `addMessage`, `appendToLastMessage`, and `setTypingIndicatorVisible` marshal through `Display.callSerially` internally. + +### One-line wiring + +```java +LlmChatBinding.bind(view, + LlmClient.openai(apiKey), + ChatRequest.builder().model("gpt-4o-mini").build()); +``` + +This wires the input bar to `chatStream(...)` and replays the conversation history on every turn — fine for prototypes, replace with a custom send pipeline when you need tool calls, structured output, or analytics. + +### Theming + +CSS-style UIIDs: + +``` +ChatView — outer container +ChatViewMessages — scrollable column +ChatBubbleUser — user bubble container +ChatBubbleAssistant — assistant bubble container +ChatBubbleSystem — system bubble container +ChatBubbleText — TextArea inside every bubble +ChatTypingIndicator — animated dots +ChatInput — input strip +ChatInputField — text field +ChatSendButton — send button +ChatAttachButton — attach button (hidden when setOnAttach not called) +ChatVoiceButton — voice button (hidden when setOnVoice not called) +``` + +Override `ChatView.createBubble(message)` to plug in a `ChatBubble` subclass with custom rendering. + +## SpeechRecognizer + +iOS uses `SFSpeechRecognizer`, Android uses `android.speech.SpeechRecognizer`. JavaSE is unsupported unless the optional `cn1-ai-whisper` cn1lib is on the classpath. + +```java +import com.codename1.media.*; + +if (!SpeechRecognizer.isSupported()) { /* degrade gracefully */ return; } + +RecognitionOptions opts = new RecognitionOptions() + .setLanguageTag("en-US") // BCP-47 + .setPartialResults(true) + .setContinuous(false) + .setMaxResults(3); + +SpeechRecognizer.recognize(opts, new RecognitionCallback.Adapter() { + @Override public void onPartialResult(String t) { chatView.getInput().setText(t); } + @Override public void onResult(String t, float confidence, String[] alternatives) { + chatView.addMessage(ChatMessage.user(t)); + } +}); + +// SpeechRecognizer.stop() // end the active session +``` + +The build-time scanner adds the `NSSpeechRecognitionUsageDescription` and `NSMicrophoneUsageDescription` `Info.plist` strings, and Android `RECORD_AUDIO` permission, automatically. + +## TextToSpeech + +iOS uses `AVSpeechSynthesizer`, Android uses `android.speech.tts.TextToSpeech`, JavaSE falls back to `say` on macOS, `espeak` on Linux, and SAPI on Windows. + +```java +import com.codename1.media.*; + +if (TextToSpeech.isSupported()) { + TtsOptions opts = new TtsOptions() + .setLanguageTag("fr-FR") + .setRate(1.0f) + .setPitch(1.0f) + .setVolume(1.0f); + TextToSpeech.speak("Bonjour", opts); +} + +// TextToSpeech.stop(); // cancel current utterance +// TextToSpeech.getAvailableVoices();// platform voice identifiers +``` + +## SecureStorage — non-prompting overloads for LLM keys + +The biometric-gated overloads (`get(reason, account)`, `set(reason, account, value)`, `remove(reason, account)`) remain the right choice for credentials a human user authenticates against. + +For things like LLM API keys, where the network layer reads the secret on every call and a biometric prompt would be unusable, use the new single-argument overloads: + +```java +SecureStorage store = SecureStorage.getInstance(); +store.set("openai.key", apiKey); // returns false when unsupported +String key = store.get("openai.key"); // returns null when absent +LlmClient client = LlmClient.openai(key); +``` + +Base class returns `null` / `false` on platforms without an implementation, so you can wire this in without a platform check. + +## ML Kit cn1libs + +Three cn1libs, each backed by ML Kit on iOS and Android. Drop the `pom` dependency in `common/pom.xml` and the build-time scanner takes care of Pods, Gradle deps, permissions, and usage strings. + +```xml + + com.codenameone + cn1-ai-mlkit-barcode-lib + ${cn1.version} + pom + +``` + +### Barcode scanner + +```java +import com.codename1.ai.mlkit.barcode.BarcodeScanner; + +Capture.capturePhoto(evt -> { + String path = (String) evt.getSource(); + byte[] bytes = readAllBytes(path); + BarcodeScanner.scan(bytes).ready(values -> { + for (String v : values) Log.p("Detected: " + v); + }); +}); +``` + +Decodes QR, EAN, Code 128, and the other ML Kit-supported formats. + +### Document scanner + +```java +import com.codename1.ai.mlkit.docscan.DocumentScanner; + +DocumentScanner.scanToFile(jpegBytes).ready(filePath -> { + // filePath points to the cropped, corrected document image +}); +``` + +iOS uses `VisionKit`. Android uses the Google Play Services document scanner — on devices without Play Services, the call resolves through `AsyncResource.except(...)`. + +### Face detector + +```java +import com.codename1.ai.mlkit.face.FaceDetector; + +FaceDetector.detect(jpegBytes).ready(rects -> { + for (int i = 0; i < rects.length; i += 4) { + int x = rects[i], y = rects[i + 1], w = rects[i + 2], h = rects[i + 3]; + // draw rectangle, crop, etc. + } +}); +``` + +Returns a packed `int[]` — four ints per face. + +## Common patterns + +### Capture photo → describe via multi-modal LLM → speak the result + +```java +Capture.capturePhoto(evt -> { + String path = (String) evt.getSource(); + byte[] bytes = readAllBytes(path); + ImagePart img = new ImagePart(bytes, "image/jpeg"); + + ChatRequest req = ChatRequest.builder() + .model("gpt-4o") + .addMessage(ChatMessage.userWithImage("Describe the photo.", img)) + .build(); + + chatView.addMessage(req.getMessages().get(0)); + chatView.beginAssistantStream(); + StringBuilder full = new StringBuilder(); + + LlmClient.openai(apiKey).chatStream(req, new StreamingListener.Adapter() { + @Override public void onContentDelta(String d) { + full.append(d); + chatView.appendToLastMessage(d); + } + }).ready(resp -> TextToSpeech.speak(full.toString())); +}); +``` + +### Voice-driven turn + +```java +view.setOnVoice(e -> SpeechRecognizer.recognize( + new RecognitionOptions().setPartialResults(true), + new RecognitionCallback.Adapter() { + @Override public void onPartialResult(String t) { + view.getInput().setText(t); + } + @Override public void onResult(String t, float c, String[] alts) { + view.getInput().setText(t); + // Trigger setOnSend manually if you want auto-send + } + })); +``` + +### Offline development against Ollama + +1. `brew install ollama && ollama pull llama3.2` +2. `ollama serve` (default port `11434`) +3. Run the simulator. Production code calling `LlmClient.openai(...)` is automatically redirected. Override with `-Dcn1.ai.simulatorRedirect=ollama -Dcn1.ai.ollamaModel=llama3.2`. + +## What NOT to do + +- **Don't reach for `Class.forName(...)` to discover providers.** Obfuscation renames classes in shipped builds; reflective name lookups work in the simulator but fail in production. The factory methods on `LlmClient` and `ImageGenerator` already give you the indirection you need. +- **Don't store an API key in source.** Use `SecureStorage.get("openai.key")` (single-arg overload) or pull it from a server-side proxy. Hard-coded keys leak through reverse-engineered binaries. +- **Don't call `chatStream` from a tight UI loop.** A streaming call holds an HTTP connection until the response completes; one per user turn is correct, one per keystroke is a bug. +- **Don't mutate `ChatView` on a non-EDT thread without going through the documented mutators.** `addMessage`, `appendToLastMessage`, and `setTypingIndicatorVisible` are thread-safe; arbitrary `view.add(...)` calls are not. +- **Don't assume the document scanner works on every Android device.** It requires Google Play Services. Wrap the call and fall back to `Capture.capturePhoto(...)` if the scanner returns an error. +- **Don't ship a project that bundles `cn1-ai-stablediffusion` to the cloud build server without checking.** The cn1lib carries multi-GB native blobs; the build will reject the upload with a `cn1.ai.requiresBigUpload` hint. Build locally for those projects. diff --git a/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java b/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java index 76b848c134..004665d483 100644 --- a/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java +++ b/scripts/initializr/common/src/test/java/com/codename1/initializr/model/GeneratorModelMatrixTest.java @@ -144,6 +144,7 @@ private void validateClaudeSkillBundled() throws Exception { ".agent-skills/codename-one/references/cn1libs.md", ".agent-skills/codename-one/references/snapshot-builds.md", ".agent-skills/codename-one/references/debugging.md", + ".agent-skills/codename-one/references/ai-and-speech.md", ".agent-skills/codename-one/tools/README.md", ".agent-skills/codename-one/tools/IsApiSupported.java", ".agent-skills/codename-one/tools/IsCssValid.java" From e280f30c5ac46ac7ecc016bbb78aa3b95cd38c93 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 28 May 2026 04:12:01 +0300 Subject: [PATCH 2/5] Reword three LanguageTool snags in the new AI chapter Local reproduction with language-tool-python==2.9.4 (the CI-pinned version) flagged three matches in Ai-And-Speech.asciidoc: - PCT_SINGULAR_NOUN_PLURAL_VERB_AGREEMENT on "the simulator, the cloud build, and your CI pipeline route everything" -- the rule wanted the singular-vs-plural agreement reworded. Rephrased to ": the simulator, the cloud builder, and your CI pipeline all run the same code." - MORFOLOGIK_RULE_EN_US on "thread-safe mutators" -- the en_US dictionary doesn't recognise "mutators". Spelled the methods out ("addMessage, appendToLastMessage, and setTypingIndicatorVisible"). - MORFOLOGIK_RULE_EN_US on "SAPI on Windows" -- the flagged span was the two-word phrase, so the accept-list entry for "SAPI" alone did not match it. Wrapped SAPI in a code span so the HTML extractor replaces it with the inline placeholder before LanguageTool sees the surrounding prose. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/developer-guide/Ai-And-Speech.asciidoc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/developer-guide/Ai-And-Speech.asciidoc b/docs/developer-guide/Ai-And-Speech.asciidoc index fc4e48b5ef..a95f6bc2f5 100644 --- a/docs/developer-guide/Ai-And-Speech.asciidoc +++ b/docs/developer-guide/Ai-And-Speech.asciidoc @@ -4,9 +4,8 @@ Codename One ships a portable LLM client, a streaming chat component, speech-to-text and text-to-speech APIs, and a family of cn1lib bridges that wire ML Kit into iOS and Android builds. All the public surface -lives next to the rest of the framework, so the simulator, the cloud -build, and your CI pipeline route everything through the same code -paths. +lives next to the rest of the framework: the simulator, the cloud +builder, and your CI pipeline all run the same code. This chapter introduces every piece in turn: @@ -353,8 +352,9 @@ input strip in one component. Drop it into a `Form` with a single .ChatView mockup image::img/chat-view-mockup.svg[ChatView mockup, 360, 640] -The component exposes thread-safe mutators, so background callbacks -from `chatStream(...)` can mutate the view directly: +The component exposes thread-safe `addMessage`, `appendToLastMessage`, +and `setTypingIndicatorVisible` methods, so background callbacks from +`chatStream(...)` can mutate the view directly: [source,java] ---- @@ -484,7 +484,8 @@ if (TextToSpeech.isSupported()) { iOS uses `AVSpeechSynthesizer`, Android uses `android.speech.tts.TextToSpeech`, and the JavaSE simulator falls back -to `say` on macOS, `espeak` on Linux, and SAPI on Windows. +to `say` on macOS, `espeak` on Linux, and the platform `SAPI` bridge on +Windows. `getAvailableVoices()` returns the platform-specific voice identifiers when the OS exposes them; `setVoiceId(...)` accepts any of those strings, or `null` to use the default voice for the configured From bfd8d224780448e2d9e7a39726c87530365cb7ba Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 28 May 2026 05:47:07 +0300 Subject: [PATCH 3/5] Replace ChatView SVG mockup with a real simulator screenshot The first revision shipped a hand-authored SVG sketch of the ChatView component. Swap it for an actual PNG produced by the JavaSE simulator running the component under the iOS Modern theme so the developer guide shows the real rendering, not a wireframe. - Adds ChatViewDevGuideScreenshotTest in scripts/hellocodenameone (under common/src/test/java, where cn1:test discovers AbstractTest implementers). The test installs iOSModernTheme.res, builds a Form with a populated ChatView, paints it to a PNG, and writes it to Storage as chat-view.png. Running it via mvn -pl javase -am test (with the test profile activator) drops ~/.cn1/chat-view.png. - Adds docs/developer-guide/img/chat-view.png, captured from a local simulator run via the test above. - Drops docs/developer-guide/img/chat-view-mockup.svg. - Points the chapter's image:: at the new PNG with caption "ChatView rendered in the JavaSE simulator under the iOS Modern theme". Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/developer-guide/Ai-And-Speech.asciidoc | 4 +- docs/developer-guide/img/chat-view-mockup.svg | 67 -------- docs/developer-guide/img/chat-view.png | Bin 0 -> 173428 bytes .../ChatViewDevGuideScreenshotTest.java | 144 ++++++++++++++++++ 4 files changed, 146 insertions(+), 69 deletions(-) delete mode 100644 docs/developer-guide/img/chat-view-mockup.svg create mode 100644 docs/developer-guide/img/chat-view.png create mode 100644 scripts/hellocodenameone/common/src/test/java/com/codenameone/examples/hellocodenameone/ChatViewDevGuideScreenshotTest.java diff --git a/docs/developer-guide/Ai-And-Speech.asciidoc b/docs/developer-guide/Ai-And-Speech.asciidoc index a95f6bc2f5..a259878af9 100644 --- a/docs/developer-guide/Ai-And-Speech.asciidoc +++ b/docs/developer-guide/Ai-And-Speech.asciidoc @@ -349,8 +349,8 @@ list of bubbles, the streaming append, the typing indicator, and the input strip in one component. Drop it into a `Form` with a single `BorderLayout.CENTER`: -.ChatView mockup -image::img/chat-view-mockup.svg[ChatView mockup, 360, 640] +.`ChatView` rendered in the JavaSE simulator under the iOS Modern theme +image::img/chat-view.png[ChatView in the JavaSE simulator, 320] The component exposes thread-safe `addMessage`, `appendToLastMessage`, and `setTypingIndicatorVisible` methods, so background callbacks from diff --git a/docs/developer-guide/img/chat-view-mockup.svg b/docs/developer-guide/img/chat-view-mockup.svg deleted file mode 100644 index 1d61a2ef4e..0000000000 --- a/docs/developer-guide/img/chat-view-mockup.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - ChatView mockup - Mockup of the com.codename1.components.ChatView component showing a system bubble, two user bubbles, two assistant bubbles, a typing indicator, and the ChatInput strip with attach, text field, voice, and send buttons. - - - - - - - Assistant - 14:32 - - - - - - - Session started - - - - Translate "good morning" to French - - - - "Bonjour" (literally "good day"). - - - - Now read it out and - show me an image. - - - - Sure. Streaming an image now - and speaking the translation - via TextToSpeech.speak(...). - - - - - - - - - - - - - - - + - - - - Message - - - - - - - - - - diff --git a/docs/developer-guide/img/chat-view.png b/docs/developer-guide/img/chat-view.png new file mode 100644 index 0000000000000000000000000000000000000000..2037a05e516142938e115f137930de76d32538b5 GIT binary patch literal 173428 zcmeFZXIK;1);8=M=g24uDhet{9Hl7=iqawIU;`<3L`p`gNC`zcBzhDO5KxLpjfjBM z2uMN+i3X4fRcRrFBB4ZT2#^9HPLIxM|bYr@w3g|EEa4vue|(N1Ke!pSu}gJ3X+q*Vn$5 z%RD@9{NChO!!!HluZN#`og?w~YW&l8zx-;S8{hFpO*~__x7^KR7sB?)FU+OpC1$#T;tkMUIO2+W0iiip_qNlH&w`5J`RJSG7&epPPP_^yli1tBOeZ|dS*WWwz6;~nP zKQ`^tH{1{YdbDL5e-<5=m zeBJc-SN~Si-!Ad@&G}yzCC(lxsh%uwD=fxLQWDIe`{BmTq0DHQNoFH1lrx}-*WUEW zr1|QWM;F~{Sv?e=%!TyKi~w3BGxuY^LYiB=UR)qLu+^28ws}*_N8sMCAI9mk?`XL9 zeH?uh`u4VM;8=jq-8K*>-xz@FZ&E!HtE-}Yhtb0#!yu~XqFbLErVI&IITXia8;yJZ zgMrQ=K6hJuwtjL@OZ@5gj^d9U#-z_dSU>RdfUS4koMf70kSk1*eXA&HTks0Aam4ig z(RTSw2_zt)kuR&;LQzDgHt1WlZK6J_>jv}s$HXj?yWGWpj(q-e&FeF`g*MVTGlLAo z^2GWtq;6hf4p;sByW0MyO($Jem&pXDg*IKd_lr(**W!xXIGGcUN(tj<5g`F8KfN{) z9wQYVG9pd-$&8@SscQF)Z$0(RU8$TFrTj$`wOi&7^MjdJHYp^4G?3ubs!lc$^PIO` z?nq|q?dyXR_%wN!EK!xpL%PFHB^0Ke4S1Fz3rDla?InMe;;09K&mR4r+uI0bmrj4X z1Zj0bg|bHpV_~0L39Br0fXn^#%}e=p|31}I^Qw9g&4v=f>T=tR#=yhQQNmtbPQOof zQdXeYXr9=gN_r`Z^02(06&C*_4M!uv+M&GZQGyipes;_@WkX%97bqRQR ztio`VcfTke!H=i9mcfxcDn*LGZE(X5_%%aUhQ*ylAR(T9{a%f}s~p(sd@^Oge<*`L zrizTzMC}>*5a;XCZ13LrctxeG7OngadaKmm&%*(A7f}K$JT{et4spcLGlTuMm}bk!Rk%P1ORaDxs#|&M(}SL$eqy$Y^mN(R z4u?y}Hzsh1QuvcqXghf5lvRRz6#u)3!81k)d^p|IV2Na2cF@27l$Bd)L$%ns=2SR- zsSHct&rl}vP?`J%l;AnT?vS}2^ZYz_p6L1BWZ&Vi;VWolvt9G?auW;#&vO!oa(~BJ zTeyb;A727d`lQ?rj~vX-S(xNd>AQyC(6e2^FNsi?HyBS`tYL?k70>_vpQ8xH^ntiNx{N`7hPgBi7&!LvT7G&iehGGWyhXDL5Cb-gp! z^hyuJmp5H*j#JM0au4!E2W%5kWD5Bs0+~QsernPWg2O_9zcK4TQwSq?=*lYg+F}Jd z^u%C=;Qn&e&yUiD#@Yq--3ck$BWm$=qVa%h(Mvb0XMM2M9*W%2&ck5vVgyxZfl&@OT_E#IP=ElTT zkTx$71bjvY`c4BisiN;ep(AR!E3XWVPQU;FS^Mh;)SpWQ+=&Y7#+r_t|$Y#-YI=Gu@2fgZdWzbJsAK;Cr!P^kP1^iaAGZ<wlbm2~JiaDsr9q*tpcwlY&k#(>Y4P->D37Csk{(_{Q=b0UJwjj5Ao}J`H<(4rf+6_nPJ&6_#~H zlOK|#Z2eeiC9U7P`2=%3hv;6nNzqnWC1h{u5ZMxleLhUWKSH@A46V=h2I0*a9@uOZ zudXmz)~ph($+$lcw0{`H@dwyWGhKNRwFW52RSjv0+br%(vXWwicZ}(@d8^1UGqnK( z*plg*A-)IB5=Qi^Wh1{`kZN7!GmwpG&}N`%kKU{nE&ZN;W+MbubmRE;W8tN3I#Y74 zlA?w&sMWzFC&ii|c#%@QhC4O*`yMJSOj;1CPoÌhSM~R}I?eBVzy0S?L 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
  • &Ga%{sa`sVd?%lj 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 V@&((i1hapIY8#y|`s3QY{|h@|Ba#3B literal 0 HcmV?d00001 diff --git a/scripts/hellocodenameone/common/src/test/java/com/codenameone/examples/hellocodenameone/ChatViewDevGuideScreenshotTest.java b/scripts/hellocodenameone/common/src/test/java/com/codenameone/examples/hellocodenameone/ChatViewDevGuideScreenshotTest.java new file mode 100644 index 0000000000..66e3de74d0 --- /dev/null +++ b/scripts/hellocodenameone/common/src/test/java/com/codenameone/examples/hellocodenameone/ChatViewDevGuideScreenshotTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone; + +import com.codename1.ai.ChatMessage; +import com.codename1.components.ChatView; +import com.codename1.io.Storage; +import com.codename1.io.Util; +import com.codename1.testing.AbstractTest; +import com.codename1.testing.TestUtils; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Image; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.plaf.UIManager; +import com.codename1.ui.util.ImageIO; +import com.codename1.ui.util.Resources; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Builds the ChatView screenshot embedded in the developer guide's "AI, + * Chat UI, and Speech" chapter. + * + * The conversation is hard-coded so the capture is reproducible. Stored + * under the override name `chat-view.png` -- the developer-guide build + * picks it up from Storage / `~/.cn1` and copies the result into + * `docs/developer-guide/img/`. + */ +public class ChatViewDevGuideScreenshotTest extends AbstractTest { + private static final String STORAGE_KEY = "chat-view.png"; + private static final long FORM_TIMEOUT_MS = 5000L; + + @Override + public boolean shouldExecuteOnEDT() { + return true; + } + + @Override + public boolean runTest() throws Exception { + installModernTheme(); + + Form chat = new Form("Assistant", new BorderLayout()); + ChatView view = new ChatView(); + chat.add(BorderLayout.CENTER, view); + + view.addMessage(ChatMessage.system("AI Travel Assistant")); + view.addMessage(ChatMessage.user( + "Plan a 3-day Lisbon trip in November under 900 euro.")); + view.addMessage(ChatMessage.assistant( + "Sure! Day 1: Alfama walking tour + fado dinner. " + + "Day 2: Belem + LX Factory. Day 3: Sintra day trip. " + + "Estimated total: 820 euro (flights from Madrid).")); + view.addMessage(ChatMessage.user("Any vegetarian dinner spots?")); + view.addMessage(ChatMessage.assistant( + "Yes - in Alfama try Boi-Cavalo (modern Portuguese, " + + "vegan menu) and Ao 26 (vegan classics).")); + view.setTypingIndicatorVisible(true); + + chat.show(); + TestUtils.waitForFormTitle("Assistant", FORM_TIMEOUT_MS); + + // Let the layout finish a frame before grabbing pixels. + TestUtils.waitFor(200); + + return saveScreenshot(chat); + } + + private void installModernTheme() throws java.io.IOException { + String platform = Display.getInstance().getPlatformName(); + String resourceName; + if ("ios".equals(platform)) { + resourceName = "/iOSModernTheme.res"; + } else if ("and".equals(platform)) { + resourceName = "/AndroidMaterialTheme.res"; + } else { + return; + } + InputStream in = openResource(resourceName); + if (in == null) { + return; + } + try { + Resources r = Resources.open(in); + String[] names = r.getThemeResourceNames(); + if (names == null || names.length == 0) { + return; + } + UIManager.getInstance().setThemeProps(r.getTheme(names[0])); + UIManager.getInstance().refreshTheme(); + } finally { + Util.cleanup(in); + } + } + + private InputStream openResource(String resourceName) { + InputStream in = Display.getInstance().getResourceAsStream(getClass(), resourceName); + if (in != null) { + return in; + } + return ChatViewDevGuideScreenshotTest.class.getResourceAsStream(resourceName); + } + + private boolean saveScreenshot(Form chat) throws Exception { + Image screenshot = Image.createImage(chat.getWidth(), chat.getHeight()); + chat.paintComponent(screenshot.getGraphics(), true); + + Storage storage = Storage.getInstance(); + if (storage.exists(STORAGE_KEY)) { + storage.deleteStorageFile(STORAGE_KEY); + } + ImageIO io = ImageIO.getImageIO(); + assertNotNull(io, "PNG image support is required for the dev-guide screenshot."); + OutputStream out = null; + try { + out = storage.createOutputStream(STORAGE_KEY); + io.save(screenshot, out, ImageIO.FORMAT_PNG, 1); + } finally { + Util.cleanup(out); + } + return true; + } +} From 203651f14eb990b5819f1d927dfa84dc8bc180bc Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 28 May 2026 10:54:36 +0300 Subject: [PATCH 4/5] Wire Anthropic and Gemini through their OpenAI-compatible endpoints The original AnthropicClient and GeminiClient were scaffolds that threw UnsupportedOperationException on every call, with documentation telling callers to route through LlmClient.localOpenAiCompatible manually. Both providers now publish first-party OpenAI-compatible endpoints that speak the canonical /chat/completions wire format: - https://api.anthropic.com/v1/chat/completions - https://generativelanguage.googleapis.com/v1beta/openai/chat/completions That is the same shape OpenAiClient already implements end-to-end -- streaming, tool calls, multi-modal image parts, structured JSON output, the entire StreamingChatRequest plumbing. So both subclasses now extend OpenAiClient and only override the provider name and the default model: - LlmClient.anthropic(key) -> claude-sonnet-4-5 - LlmClient.gemini(key) -> gemini-2.0-flash LlmClient.DEFAULT_GEMINI_URL is bumped to the OpenAI-compat path (`/v1beta/openai`). AnthropicClient still overrides embed() with the helpful "Anthropic doesn't publish embeddings; use Voyage AI" error because that part remains true. Documentation updated: the AI chapter and the initializr authoring skill no longer hedge with "throws an LlmException until native clients land"; both providers work out of the box, with a default- model table per provider added to the skill. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/com/codename1/ai/AnthropicClient.java | 51 ++++++---------- .../src/com/codename1/ai/GeminiClient.java | 59 +++++-------------- .../src/com/codename1/ai/LlmClient.java | 15 ++--- docs/developer-guide/Ai-And-Speech.asciidoc | 19 +++--- .../skill/references/ai-and-speech.md | 23 ++++++-- 5 files changed, 72 insertions(+), 95 deletions(-) diff --git a/CodenameOne/src/com/codename1/ai/AnthropicClient.java b/CodenameOne/src/com/codename1/ai/AnthropicClient.java index f01735444c..c28584f777 100644 --- a/CodenameOne/src/com/codename1/ai/AnthropicClient.java +++ b/CodenameOne/src/com/codename1/ai/AnthropicClient.java @@ -24,22 +24,25 @@ import com.codename1.util.AsyncResource; -/// Anthropic /v1/messages client. Wire format differs from OpenAI in -/// three important ways: system messages live in a top-level `system` -/// string rather than a role; image parts use `{type:"image", source: -/// {type:"base64", media_type, data}}`; tool calls stream argument -/// JSON via `input_json_delta` events. +/// Anthropic Messages client. /// -/// This is currently a scaffold -- the full request/response mapping -/// is tracked as a follow-up. The class compiles and registers under -/// `LlmClient.anthropic(...)` so app code using the API can be built; -/// runtime calls throw a clear `UnsupportedOperationException`. -class AnthropicClient extends LlmClient { - private final String apiKey; +/// Talks to `https://api.anthropic.com/v1/chat/completions`, the +/// official OpenAI-compatible Messages shim. The wire format on that +/// endpoint is the same `/v1/chat/completions` shape that OpenAI and +/// every OpenAI-compatible provider speaks, so this client inherits the +/// full streaming, tool-call, response-format and image-attachment +/// implementation from `OpenAiClient`. Only the provider name, default +/// model, and the embeddings shape (Anthropic does not publish a +/// first-party embeddings endpoint) are overridden here. +/// +/// Authentication uses `Authorization: Bearer sk-ant-...` -- identical +/// header layout to OpenAI -- which is why the inherited request +/// configuration works without modification. +class AnthropicClient extends OpenAiClient { AnthropicClient(String apiKey, String baseUrl) { - super(baseUrl); - this.apiKey = apiKey; + super(apiKey, baseUrl); + setDefaultModel("claude-sonnet-4-5"); } @Override @@ -47,30 +50,14 @@ public String getProvider() { return "anthropic"; } - @Override - public AsyncResource chat(ChatRequest req) { - AsyncResource r = new AsyncResource(); - r.error(new UnsupportedOperationException( - "AnthropicClient is not yet implemented in this release. " - + "Use LlmClient.openai(...) or run the model behind an OpenAI-compatible proxy.")); - return r; - } - - @Override - public AsyncResource chatStream(ChatRequest req, StreamingListener listener) { - return chat(req); - } - @Override public AsyncResource embed(EmbeddingRequest req) { AsyncResource r = new AsyncResource(); r.error(new UnsupportedOperationException( "Anthropic does not publish a first-party embeddings endpoint. " - + "Use a Voyage AI key via LlmClient.localOpenAiCompatible(\"https://api.voyageai.com/v1\", key, model).")); + + "Use a Voyage AI key via LlmClient.localOpenAiCompatible(" + + "\"https://api.voyageai.com/v1\", key, \"voyage-3\") or " + + "LlmClient.openai(...) with text-embedding-3-small.")); return r; } - - String getApiKey() { - return apiKey; - } } diff --git a/CodenameOne/src/com/codename1/ai/GeminiClient.java b/CodenameOne/src/com/codename1/ai/GeminiClient.java index 930aad4f8c..87c823b745 100644 --- a/CodenameOne/src/com/codename1/ai/GeminiClient.java +++ b/CodenameOne/src/com/codename1/ai/GeminiClient.java @@ -22,56 +22,29 @@ */ package com.codename1.ai; -import com.codename1.util.AsyncResource; - -/// Google Gemini client. The native wire format diverges from OpenAI's: -/// system messages live in `systemInstruction`, content is split into -/// `parts` with `inline_data` / `text`, tool calls arrive atomically -/// at stream end rather than fragment-by-fragment. +/// Google Gemini client. +/// +/// Talks to `https://generativelanguage.googleapis.com/v1beta/openai/`, +/// Google's official OpenAI-compatible shim. The endpoint accepts the +/// standard `/chat/completions` and `/embeddings` shape -- including +/// streaming, tool calls, multi-modal image parts, and structured +/// JSON output -- so this client inherits the full +/// implementation from `OpenAiClient` and only overrides the provider +/// name and default model. /// -/// Google publishes an OpenAI-compatibility endpoint at -/// `https://generativelanguage.googleapis.com/v1beta/openai/` that -/// works with [LlmClient#localOpenAiCompatible] today; this dedicated -/// client (which handles the native shape end-to-end) is a follow-up. -class GeminiClient extends LlmClient { - private final String apiKey; +/// Authentication uses `Authorization: Bearer `, +/// identical to the OpenAI header layout. Models are addressed by +/// their public identifiers (`gemini-2.0-flash`, `gemini-2.5-pro`, +/// `gemini-2.5-flash`, ...). +class GeminiClient extends OpenAiClient { GeminiClient(String apiKey, String baseUrl) { - super(baseUrl); - this.apiKey = apiKey; + super(apiKey, baseUrl); + setDefaultModel("gemini-2.0-flash"); } @Override public String getProvider() { return "gemini"; } - - @Override - public AsyncResource chat(ChatRequest req) { - AsyncResource r = new AsyncResource(); - r.error(new UnsupportedOperationException( - "GeminiClient (native) is not yet implemented in this release. " - + "Use LlmClient.localOpenAiCompatible(" - + "\"https://generativelanguage.googleapis.com/v1beta/openai\", apiKey, model) " - + "to reach Gemini through Google's OpenAI-compatible shim.")); - return r; - } - - @Override - public AsyncResource chatStream(ChatRequest req, StreamingListener listener) { - return chat(req); - } - - @Override - public AsyncResource embed(EmbeddingRequest req) { - AsyncResource r = new AsyncResource(); - r.error(new UnsupportedOperationException( - "GeminiClient.embed is not yet implemented. Use the OpenAI-compatible shim " - + "or LlmClient.openai(...) with text-embedding-3-small.")); - return r; - } - - String getApiKey() { - return apiKey; - } } diff --git a/CodenameOne/src/com/codename1/ai/LlmClient.java b/CodenameOne/src/com/codename1/ai/LlmClient.java index 73333c86c3..fea905929a 100644 --- a/CodenameOne/src/com/codename1/ai/LlmClient.java +++ b/CodenameOne/src/com/codename1/ai/LlmClient.java @@ -58,15 +58,16 @@ public abstract class LlmClient { // uses. // // Versions reflect the providers' production REST shapes as of - // mid-2026: - // - OpenAI Chat Completions -- /v1 (stable) - // - Anthropic Messages -- /v1 (stable) - // - Google Gemini (native) -- /v1beta (only path - // that exposes streaming generateContent and tool calls today) - // - Ollama OpenAI-compat shim -- /v1 + // mid-2026. Anthropic and Gemini both publish an OpenAI-compatible + // `/chat/completions` endpoint, which is what this client targets + // -- one shared wire format, three providers. + // - OpenAI Chat Completions -- /v1 + // - Anthropic Messages compat -- /v1 + // - Google Gemini OpenAI compat -- /v1beta/openai + // - Ollama OpenAI-compat shim -- /v1 public static final String DEFAULT_OPENAI_URL = "https://api.openai.com/v1"; public static final String DEFAULT_ANTHROPIC_URL = "https://api.anthropic.com/v1"; - public static final String DEFAULT_GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta"; + public static final String DEFAULT_GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/openai"; public static final String DEFAULT_OLLAMA_URL = "http://localhost:11434/v1"; private String baseUrl; diff --git a/docs/developer-guide/Ai-And-Speech.asciidoc b/docs/developer-guide/Ai-And-Speech.asciidoc index a259878af9..462fa7a98f 100644 --- a/docs/developer-guide/Ai-And-Speech.asciidoc +++ b/docs/developer-guide/Ai-And-Speech.asciidoc @@ -53,17 +53,22 @@ LlmClient together = LlmClient.localOpenAiCompatible( apiKey, "meta-llama/Llama-3.3-70B-Instruct-Turbo"); -// Scaffolded; native wire format pending. Today these throw a clear -// error pointing callers at the OpenAI-compatible shim. +// Anthropic and Google Gemini, both via their OpenAI-compatible +// endpoints. The wire format is identical; only the base URL, +// default model, and auth differ. LlmClient anthropic = LlmClient.anthropic(apiKey); LlmClient gemini = LlmClient.gemini(apiKey); ---- -The OpenAI client is the fully implemented native client. It also drives -Ollama, vLLM, and `llama.cpp` because the wire format matches. The -Anthropic and Gemini factories compile and register, but throw an -`LlmException` until their native clients land, so route through -`localOpenAiCompatible` if you need them today. +All five factories return the same `LlmClient` API. OpenAI, Ollama, +vLLM, and `llama.cpp` share the canonical wire format. Anthropic's +`https://api.anthropic.com/v1/chat/completions` and Google's +`https://generativelanguage.googleapis.com/v1beta/openai/chat/completions` +implement the same shape, so the framework speaks to all three through +one network layer. Default models pick a sensible production-grade +target per provider (`gpt-4o-mini`, `claude-sonnet-4-5`, +`gemini-2.0-flash`, `llama3.2`); override per request with +`ChatRequest.builder().model(...)`. ==== A first chat diff --git a/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md b/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md index cf6fbdb27a..187f63cf54 100644 --- a/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md +++ b/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md @@ -24,8 +24,8 @@ The build-time scanner in the Codename One Maven plugin (`AiDependencyTable`) pi ```java import com.codename1.ai.*; -// OpenAI (the fully implemented native client). Also drives Ollama, -// vLLM, llama.cpp, Together, Groq, Fireworks etc. via shared wire format. +// OpenAI. Also drives Ollama, vLLM, llama.cpp, Together, Groq, +// Fireworks etc. via shared wire format. LlmClient client = LlmClient.openai(apiKey); // Local Ollama on http://localhost:11434 @@ -35,12 +35,23 @@ LlmClient local = LlmClient.ollama("llama3.2"); LlmClient together = LlmClient.localOpenAiCompatible( "https://api.together.xyz/v1", apiKey, "meta-llama/Llama-3.3-70B-Instruct-Turbo"); -// Scaffolds today: throw a clear error until native clients land. -// Route via localOpenAiCompatible() instead. -LlmClient.anthropic(apiKey); -LlmClient.gemini(apiKey); +// Anthropic + Gemini route through their OpenAI-compatible endpoints +// (Anthropic at /v1/chat/completions, Gemini at /v1beta/openai/ +// chat/completions). Same ChatRequest / ChatResponse value types, +// same streaming, same tool-call API. +LlmClient claude = LlmClient.anthropic(apiKey); // default model: claude-sonnet-4-5 +LlmClient gemini = LlmClient.gemini(apiKey); // default model: gemini-2.0-flash ``` +Default models per provider when `ChatRequest.builder().model(...)` is not called: + +| Provider | Default model | Override via | +| --- | --- | --- | +| OpenAI | `gpt-4o-mini` | `ChatRequest.builder().model("gpt-4o")` etc. | +| Anthropic | `claude-sonnet-4-5` | `ChatRequest.builder().model("claude-opus-4-1")` etc. | +| Gemini | `gemini-2.0-flash` | `ChatRequest.builder().model("gemini-2.5-pro")` etc. | +| Ollama | `llama3.2` | `LlmClient.ollama("qwen2.5-7b")` or per request | + `ChatRequest` is a fluent builder. `chat()` returns the response, `chatStream()` streams deltas: ```java From d1f7fb4d8e90892f07e8684a9ddc4af48a102821 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 28 May 2026 15:18:04 +0300 Subject: [PATCH 5/5] Reframe the API-key storage section as a WARNING admonition The previous wording explained two SecureStorage code paths (biometric-gated vs non-prompting) and ended with "remain the right choice for credentials a human user authenticates against," which read more like an API tour than safety guidance. The actual risk that needs surfacing is that hard-coded keys in a mobile binary are extractable. Replace the prose block with an asciidoc WARNING admonition that spells out the rule (no key in source / resource / git, fetch from a server the user authenticates against, then cache locally with the non-prompting overloads) and keeps just the short SecureStorage code sample. Mirror the same simplification in the initializr authoring skill. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/developer-guide/Ai-And-Speech.asciidoc | 19 +++++++------------ .../skill/references/ai-and-speech.md | 6 ++---- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/docs/developer-guide/Ai-And-Speech.asciidoc b/docs/developer-guide/Ai-And-Speech.asciidoc index 462fa7a98f..3aa719bc89 100644 --- a/docs/developer-guide/Ai-And-Speech.asciidoc +++ b/docs/developer-guide/Ai-And-Speech.asciidoc @@ -326,27 +326,22 @@ local model without any conditional wiring at the call site. ==== Storing the API key -Production builds need somewhere to keep the provider API key. The -non-prompting `SecureStorage` overloads (added in the same release as -the AI surface) are designed for this: a single string argument, no -biometric prompt, return `null` or `false` on platforms that lack -support: +WARNING: Never hard-code a provider API key in source, ship it in a +bundled resource, or commit it to git. Mobile binaries are trivially +reverse-engineered; any key embedded in the app is a key your users +can extract. Fetch the key from a server endpoint that the user +authenticates against, then cache it locally with the non-prompting +`SecureStorage` overloads: [source,java] ---- SecureStorage store = SecureStorage.getInstance(); -store.set("openai.key", apiKey); // returns false when unsupported +store.set("openai.key", apiKeyFromServer); // returns false when unsupported String key = store.get("openai.key"); // returns null when absent LlmClient client = LlmClient.openai(key); ---- -The biometric-gated overloads (`get(reason, account)`, -`set(reason, account, value)`, `remove(reason, account)`) remain the -right choice for credentials a human user authenticates against. Use the -non-prompting overloads only for secrets the network layer reads on -every request, where a biometric prompt would be unusable. - === The `ChatView` component `ChatView` is a scrollable, theme-aware chat surface that handles the diff --git a/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md b/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md index 187f63cf54..330d62b3b5 100644 --- a/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md +++ b/scripts/initializr/common/src/main/resources/skill/references/ai-and-speech.md @@ -296,13 +296,11 @@ if (TextToSpeech.isSupported()) { ## SecureStorage — non-prompting overloads for LLM keys -The biometric-gated overloads (`get(reason, account)`, `set(reason, account, value)`, `remove(reason, account)`) remain the right choice for credentials a human user authenticates against. - -For things like LLM API keys, where the network layer reads the secret on every call and a biometric prompt would be unusable, use the new single-argument overloads: +**Never hard-code a provider API key in source, ship it in a bundled resource, or commit it to git.** Mobile binaries are trivially reverse-engineered; any key embedded in the app is a key your users can extract. Fetch the key from a server endpoint the user authenticates against, then cache it locally with the non-prompting `SecureStorage` overloads: ```java SecureStorage store = SecureStorage.getInstance(); -store.set("openai.key", apiKey); // returns false when unsupported +store.set("openai.key", apiKeyFromServer); // returns false when unsupported String key = store.get("openai.key"); // returns null when absent LlmClient client = LlmClient.openai(key); ```