diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f760034d..5ed52fab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,8 +33,8 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - name: Compile - run: ./gradlew compileJava + - name: Compile SDK and examples + run: ./gradlew compileJava compileExamples test: name: Unit Tests (Java ${{ matrix.java-version }}) diff --git a/.github/workflows/tests-daily.yml b/.github/workflows/tests-daily.yml index fb4a604a..48a16b65 100644 --- a/.github/workflows/tests-daily.yml +++ b/.github/workflows/tests-daily.yml @@ -36,8 +36,8 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - name: Compile - run: ./gradlew compileJava + - name: Compile SDK and examples + run: ./gradlew compileJava compileExamples unit-tests: name: Unit Tests diff --git a/README.md b/README.md index f8c5ea99..9f7f904e 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,10 @@ The SDK supports API Key authentication with automatic environment variable load import com.deepgram.DeepgramClient; // Using environment variable (DEEPGRAM_API_KEY) -DeepgramClient client = DeepgramClient.builder().build(); +DeepgramClient envClient = DeepgramClient.builder().build(); // Using API key directly -DeepgramClient client = DeepgramClient.builder() +DeepgramClient explicitClient = DeepgramClient.builder() .apiKey("YOUR_DEEPGRAM_API_KEY") .build(); ``` @@ -220,13 +220,20 @@ Stream audio for real-time speech-to-text. ```java import com.deepgram.DeepgramClient; +import com.deepgram.resources.listen.v1.types.ListenV1CloseStream; +import com.deepgram.resources.listen.v1.types.ListenV1CloseStreamType; import com.deepgram.resources.listen.v1.websocket.V1WebSocketClient; import com.deepgram.resources.listen.v1.websocket.V1ConnectOptions; import com.deepgram.types.ListenV1Model; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import okio.ByteString; DeepgramClient client = DeepgramClient.builder().build(); +byte[] audioBytes = Files.readAllBytes(Path.of("audio.wav")); -V1WebSocketClient ws = client.listen().v1().websocket(); +V1WebSocketClient ws = client.listen().v1().v1WebSocket(); // Register event handlers ws.onResults(results -> { @@ -247,9 +254,14 @@ ws.onError(error -> { // Connect with options (model is required) ws.connect(V1ConnectOptions.builder() .model(ListenV1Model.NOVA3) - .build()); + .build()) + .get(10, TimeUnit.SECONDS); -ws.send(audioBytes); +ws.sendMedia(ByteString.of(audioBytes)); +ws.sendCloseStream(ListenV1CloseStream.builder() + .type(ListenV1CloseStreamType.CLOSE_STREAM) + .build()) + .get(5, TimeUnit.SECONDS); // Close when done ws.close(); @@ -261,15 +273,25 @@ Stream text for real-time audio generation. ```java import com.deepgram.DeepgramClient; +import com.deepgram.resources.speak.v1.types.SpeakV1Close; +import com.deepgram.resources.speak.v1.types.SpeakV1CloseType; +import com.deepgram.resources.speak.v1.types.SpeakV1Flush; +import com.deepgram.resources.speak.v1.types.SpeakV1FlushType; +import com.deepgram.resources.speak.v1.types.SpeakV1Text; import com.deepgram.resources.speak.v1.websocket.V1WebSocketClient; +import java.io.ByteArrayOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; DeepgramClient client = DeepgramClient.builder().build(); +ByteArrayOutputStream audioBuffer = new ByteArrayOutputStream(); -var ttsWs = client.speak().v1().websocket(); +V1WebSocketClient ttsWs = client.speak().v1().v1WebSocket(); // Register event handlers -ttsWs.onAudioData(audioData -> { - // Process audio chunks as they arrive +ttsWs.onSpeakV1Audio(audioData -> { + audioBuffer.writeBytes(audioData.toByteArray()); }); ttsWs.onMetadata(metadata -> { @@ -281,8 +303,23 @@ ttsWs.onError(error -> { }); // Connect and send text -ttsWs.connect(); -ttsWs.send("Hello, this is streamed text-to-speech."); +ttsWs.connect().get(10, TimeUnit.SECONDS); +ttsWs.sendText(SpeakV1Text.builder() + .text("Hello, this is streamed text-to-speech.") + .build()) + .get(5, TimeUnit.SECONDS); +ttsWs.sendFlush(SpeakV1Flush.builder() + .type(SpeakV1FlushType.FLUSH) + .build()) + .get(5, TimeUnit.SECONDS); + +Thread.sleep(2000); +Files.write(Path.of("output.wav"), audioBuffer.toByteArray()); + +ttsWs.sendClose(SpeakV1Close.builder() + .type(SpeakV1CloseType.CLOSE) + .build()) + .get(5, TimeUnit.SECONDS); // Close when done ttsWs.close(); @@ -294,24 +331,58 @@ Connect to Deepgram's voice agent for real-time conversational AI. ```java import com.deepgram.DeepgramClient; +import com.deepgram.resources.agent.v1.types.AgentV1InjectUserMessage; +import com.deepgram.resources.agent.v1.types.AgentV1Settings; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgent; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItem; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItemProvider; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAudio; import com.deepgram.resources.agent.v1.websocket.V1WebSocketClient; +import com.deepgram.types.OpenAiThinkProvider; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; DeepgramClient client = DeepgramClient.builder().build(); -var agentWs = client.agent().v1().websocket(); +V1WebSocketClient agentWs = client.agent().v1().v1WebSocket(); // Register event handlers agentWs.onWelcome(welcome -> { System.out.println("Agent connected"); + + agentWs.sendSettings(AgentV1Settings.builder() + .audio(AgentV1SettingsAudio.builder().build()) + .agent(AgentV1SettingsAgent.builder() + .think(AgentV1SettingsAgentThink.of(List.of( + AgentV1SettingsAgentThinkOneItem.builder() + .provider(AgentV1SettingsAgentThinkOneItemProvider.of( + OpenAiThinkProvider.of(Map.of("model", "gpt-4o-mini")))) + .prompt("You are a helpful voice assistant. Keep responses brief.") + .build()))) + .greeting("Hello! How can I help you today?") + .build()) + .build()); +}); + +agentWs.onSettingsApplied(applied -> { + agentWs.sendInjectUserMessage(AgentV1InjectUserMessage.builder() + .content("What is the capital of France?") + .build()); +}); + +agentWs.onConversationText(text -> { + System.out.printf("[%s] %s%n", text.getRole(), text.getContent()); }); agentWs.onError(error -> { System.err.println("Error: " + error.getMessage()); }); -// Connect and interact -agentWs.connect(); -agentWs.send(audioBytes); +// Connect and wait for the agent to respond +agentWs.connect().get(10, TimeUnit.SECONDS); +Thread.sleep(5000); // Close when done agentWs.close(); @@ -330,13 +401,22 @@ Use the separate [`deepgram-sagemaker`](https://github.com/deepgram/deepgram-jav ```groovy dependencies { implementation 'com.deepgram:deepgram-java-sdk:0.2.1' // x-release-please-version - implementation 'com.deepgram:deepgram-sagemaker:0.1.0' + implementation 'com.deepgram:deepgram-sagemaker:0.1.2' } ``` ```java +import com.deepgram.DeepgramClient; import com.deepgram.sagemaker.SageMakerConfig; import com.deepgram.sagemaker.SageMakerTransportFactory; +import com.deepgram.resources.listen.v1.websocket.V1ConnectOptions; +import com.deepgram.types.ListenV1Model; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import okio.ByteString; + +byte[] audioBytes = Files.readAllBytes(Path.of("audio.wav")); var factory = new SageMakerTransportFactory( SageMakerConfig.builder() @@ -351,10 +431,11 @@ DeepgramClient client = DeepgramClient.builder() .build(); // Use the SDK exactly as normal — the transport is transparent -var ws = client.listen().v1().websocket(); +var ws = client.listen().v1().v1WebSocket(); ws.onResults(results -> { /* ... */ }); -ws.connect(V1ConnectOptions.builder().model(ListenV1Model.NOVA3).build()); -ws.sendMedia(audioBytes); +ws.connect(V1ConnectOptions.builder().model(ListenV1Model.NOVA3).build()) + .get(10, TimeUnit.SECONDS); +ws.sendMedia(ByteString.of(audioBytes)); ``` See the [SageMaker example](examples/sagemaker/LiveStreamingSageMaker.java) for a complete walkthrough. @@ -511,7 +592,6 @@ import com.deepgram.resources.listen.v1.media.types.MediaTranscribeResponse; DeepgramApiHttpResponse rawResponse = client.listen().v1().media().withRawResponse().transcribeUrl(request); -int statusCode = rawResponse.statusCode(); var headers = rawResponse.headers(); MediaTranscribeResponse body = rawResponse.body(); ``` @@ -524,13 +604,13 @@ The SDK provides comprehensive access to Deepgram's APIs: ```java client.listen().v1().media().transcribeUrl(request) // Transcribe audio from URL client.listen().v1().media().transcribeFile(body) // Transcribe audio from file bytes -client.listen().v1().websocket() // Real-time streaming transcription +client.listen().v1().v1WebSocket() // Real-time streaming transcription ``` ### Speak (Text-to-Speech) ```java client.speak().v1().audio().generate(request) // Generate speech from text -client.speak().v1().websocket() // Real-time streaming TTS +client.speak().v1().v1WebSocket() // Real-time streaming TTS ``` ### Read (Text Intelligence) @@ -541,7 +621,7 @@ client.read().v1().text().analyze(request) // Analyze text content ### Agent (Voice Agent) ```java client.agent().v1().settings().think().models().list() // List available agent models -client.agent().v1().websocket() // Real-time agent WebSocket +client.agent().v1().v1WebSocket() // Real-time agent WebSocket ``` ### Manage (Project Management) diff --git a/build.gradle b/build.gradle index 4036a5e0..431092a4 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,8 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0' testImplementation 'org.assertj:assertj-core:3.25.3' + testImplementation 'com.deepgram:deepgram-sagemaker:0.1.2' + } test { @@ -78,8 +80,7 @@ sourceSets { examples { java { srcDir 'examples' - // Exclude examples with known issues (see ISSUES section below) - exclude 'agent/**' // Blocked: missing AgentV1UpdateThink type + // Exclude examples that still need API updates. exclude 'manage/ListModels.java' // Duplicate class name with agent/ListModels exclude 'manage/MemberPermissions.java' // getScopes() not in generated API exclude 'manage/UsageBreakdown.java' // getModels() return type mismatch @@ -89,6 +90,11 @@ sourceSets { } } +dependencies { + // Optional dependencies needed by example source set + examplesImplementation 'com.deepgram:deepgram-sagemaker:0.1.2' +} + // Compile all examples tasks.register('compileExamples') { dependsOn 'examplesClasses' @@ -123,4 +129,3 @@ spotless { removeUnusedImports() } } - diff --git a/examples/agent/CustomProviders.java b/examples/agent/CustomProviders.java index a15a7672..af8d90d8 100644 --- a/examples/agent/CustomProviders.java +++ b/examples/agent/CustomProviders.java @@ -1,19 +1,19 @@ import com.deepgram.DeepgramClient; import com.deepgram.resources.agent.v1.types.AgentV1Settings; import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgent; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContext; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContextSpeak; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContextThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeak; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeakEndpoint; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeakEndpointProvider; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeakOneItemProviderDeepgramModel; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItem; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItemProvider; import com.deepgram.resources.agent.v1.types.AgentV1SettingsAudio; +import com.deepgram.resources.agent.v1.types.Deepgram; import com.deepgram.resources.agent.v1.websocket.V1WebSocketClient; import com.deepgram.types.Anthropic; -import com.deepgram.types.AnthropicThinkProviderModel; -import com.deepgram.types.Deepgram; -import com.deepgram.types.DeepgramSpeakProviderModel; -import com.deepgram.types.SpeakSettingsV1; -import com.deepgram.types.SpeakSettingsV1Provider; -import com.deepgram.types.ThinkSettingsV1; -import com.deepgram.types.ThinkSettingsV1Provider; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -57,34 +57,30 @@ public static void main(String[] args) { try { // Configure Anthropic as the think provider - Anthropic anthropicProvider = Anthropic.builder() - .model(AnthropicThinkProviderModel.CLAUDE_SONNET420250514) - .build(); - - ThinkSettingsV1 thinkSettings = ThinkSettingsV1.builder() - .provider(ThinkSettingsV1Provider.anthropic(anthropicProvider)) - .prompt("You are a helpful assistant. Keep responses concise.") - .build(); + Anthropic anthropicProvider = Anthropic.of(Map.of("model", "claude-sonnet-4-20250514")); // Configure Deepgram as the speak provider Deepgram deepgramSpeakProvider = Deepgram.builder() - .model(DeepgramSpeakProviderModel.AURA2ASTERIA_EN) - .build(); - - SpeakSettingsV1 speakSettings = SpeakSettingsV1.builder() - .provider(SpeakSettingsV1Provider.deepgram(deepgramSpeakProvider)) + .model(AgentV1SettingsAgentSpeakOneItemProviderDeepgramModel.AURA2ASTERIA_EN) .build(); - // Build agent settings with both providers - AgentV1SettingsAgentContext agentContext = AgentV1SettingsAgentContext.builder() - .think(AgentV1SettingsAgentContextThink.of(thinkSettings)) - .speak(AgentV1SettingsAgentContextSpeak.of(speakSettings)) + AgentV1SettingsAgentSpeak speakSettings = + AgentV1SettingsAgentSpeak.of(AgentV1SettingsAgentSpeakEndpoint.builder() + .provider(AgentV1SettingsAgentSpeakEndpointProvider.deepgram(deepgramSpeakProvider)) + .build()); + + AgentV1SettingsAgent agentConfig = AgentV1SettingsAgent.builder() + .think(AgentV1SettingsAgentThink.of(List.of(AgentV1SettingsAgentThinkOneItem.builder() + .provider(AgentV1SettingsAgentThinkOneItemProvider.of(anthropicProvider)) + .prompt("You are a helpful assistant. Keep responses concise.") + .build()))) + .speak(speakSettings) .greeting("Hello! I'm powered by Anthropic Claude with Deepgram voices.") .build(); AgentV1Settings settings = AgentV1Settings.builder() .audio(AgentV1SettingsAudio.builder().build()) - .agent(AgentV1SettingsAgent.of(agentContext)) + .agent(agentConfig) .build(); wsClient.sendSettings(settings); diff --git a/examples/agent/InjectMessage.java b/examples/agent/InjectMessage.java index a1cfea2a..6f62df04 100644 --- a/examples/agent/InjectMessage.java +++ b/examples/agent/InjectMessage.java @@ -3,14 +3,14 @@ import com.deepgram.resources.agent.v1.types.AgentV1InjectUserMessage; import com.deepgram.resources.agent.v1.types.AgentV1Settings; import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgent; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContext; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContextThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItem; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItemProvider; import com.deepgram.resources.agent.v1.types.AgentV1SettingsAudio; import com.deepgram.resources.agent.v1.websocket.V1WebSocketClient; import com.deepgram.types.OpenAiThinkProvider; -import com.deepgram.types.OpenAiThinkProviderModel; -import com.deepgram.types.ThinkSettingsV1; -import com.deepgram.types.ThinkSettingsV1Provider; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -52,23 +52,20 @@ public static void main(String[] args) { try { // Configure the agent - OpenAiThinkProvider openAiProvider = OpenAiThinkProvider.builder() - .model(OpenAiThinkProviderModel.GPT4O_MINI) - .build(); - - ThinkSettingsV1 thinkSettings = ThinkSettingsV1.builder() - .provider(ThinkSettingsV1Provider.openAi(openAiProvider)) - .prompt("You are a helpful voice assistant. Keep responses brief and conversational.") - .build(); - - AgentV1SettingsAgentContext agentContext = AgentV1SettingsAgentContext.builder() - .think(AgentV1SettingsAgentContextThink.of(thinkSettings)) - .greeting("Hello! I'm ready to chat.") - .build(); + OpenAiThinkProvider openAiProvider = OpenAiThinkProvider.of(Map.of("model", "gpt-4o-mini")); AgentV1Settings settings = AgentV1Settings.builder() .audio(AgentV1SettingsAudio.builder().build()) - .agent(AgentV1SettingsAgent.of(agentContext)) + .agent(AgentV1SettingsAgent.builder() + .think(AgentV1SettingsAgentThink.of( + List.of(AgentV1SettingsAgentThinkOneItem.builder() + .provider( + AgentV1SettingsAgentThinkOneItemProvider.of(openAiProvider)) + .prompt( + "You are a helpful voice assistant. Keep responses brief and conversational.") + .build()))) + .greeting("Hello! I'm ready to chat.") + .build()) .build(); wsClient.sendSettings(settings); diff --git a/examples/agent/ProviderCombinations.java b/examples/agent/ProviderCombinations.java index 9f8e3234..54ecf1ab 100644 --- a/examples/agent/ProviderCombinations.java +++ b/examples/agent/ProviderCombinations.java @@ -1,18 +1,17 @@ -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContext; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContextSpeak; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContextThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgent; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeak; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeakEndpoint; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeakEndpointProvider; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentSpeakOneItemProviderDeepgramModel; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItem; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItemProvider; +import com.deepgram.resources.agent.v1.types.Deepgram; import com.deepgram.types.Anthropic; -import com.deepgram.types.AnthropicThinkProviderModel; -import com.deepgram.types.Deepgram; -import com.deepgram.types.DeepgramSpeakProviderModel; import com.deepgram.types.Google; -import com.deepgram.types.GoogleThinkProviderModel; import com.deepgram.types.OpenAiThinkProvider; -import com.deepgram.types.OpenAiThinkProviderModel; -import com.deepgram.types.SpeakSettingsV1; -import com.deepgram.types.SpeakSettingsV1Provider; -import com.deepgram.types.ThinkSettingsV1; -import com.deepgram.types.ThinkSettingsV1Provider; +import java.util.List; +import java.util.Map; /** * Demonstrates building different provider combination configurations for comparison. Shows how to configure OpenAI, @@ -28,25 +27,23 @@ public static void main(String[] args) { // Shared speak provider (Deepgram TTS) Deepgram deepgramSpeak = Deepgram.builder() - .model(DeepgramSpeakProviderModel.AURA2ASTERIA_EN) - .build(); - SpeakSettingsV1 speakSettings = SpeakSettingsV1.builder() - .provider(SpeakSettingsV1Provider.deepgram(deepgramSpeak)) + .model(AgentV1SettingsAgentSpeakOneItemProviderDeepgramModel.AURA2ASTERIA_EN) .build(); + AgentV1SettingsAgentSpeak speakSettings = + AgentV1SettingsAgentSpeak.of(AgentV1SettingsAgentSpeakEndpoint.builder() + .provider(AgentV1SettingsAgentSpeakEndpointProvider.deepgram(deepgramSpeak)) + .build()); // Combination 1: OpenAI GPT-4o Mini + Deepgram System.out.println("=== Combination 1: OpenAI + Deepgram ==="); - OpenAiThinkProvider openAiProvider = OpenAiThinkProvider.builder() - .model(OpenAiThinkProviderModel.GPT4O_MINI) - .build(); - ThinkSettingsV1 openAiThink = ThinkSettingsV1.builder() - .provider(ThinkSettingsV1Provider.openAi(openAiProvider)) - .prompt("You are a helpful assistant powered by OpenAI.") - .build(); + OpenAiThinkProvider openAiProvider = OpenAiThinkProvider.of(Map.of("model", "gpt-4o-mini")); - AgentV1SettingsAgentContext openAiConfig = AgentV1SettingsAgentContext.builder() - .think(AgentV1SettingsAgentContextThink.of(openAiThink)) - .speak(AgentV1SettingsAgentContextSpeak.of(speakSettings)) + AgentV1SettingsAgent openAiConfig = AgentV1SettingsAgent.builder() + .think(AgentV1SettingsAgentThink.of(List.of(AgentV1SettingsAgentThinkOneItem.builder() + .provider(AgentV1SettingsAgentThinkOneItemProvider.of(openAiProvider)) + .prompt("You are a helpful assistant powered by OpenAI.") + .build()))) + .speak(speakSettings) .greeting("Hello! I'm powered by OpenAI GPT-4o Mini.") .build(); System.out.println(" Think: OpenAI GPT-4o Mini"); @@ -56,17 +53,14 @@ public static void main(String[] args) { // Combination 2: Anthropic Claude + Deepgram System.out.println("=== Combination 2: Anthropic + Deepgram ==="); - Anthropic anthropicProvider = Anthropic.builder() - .model(AnthropicThinkProviderModel.CLAUDE_SONNET420250514) - .build(); - ThinkSettingsV1 anthropicThink = ThinkSettingsV1.builder() - .provider(ThinkSettingsV1Provider.anthropic(anthropicProvider)) - .prompt("You are a helpful assistant powered by Anthropic Claude.") - .build(); + Anthropic anthropicProvider = Anthropic.of(Map.of("model", "claude-sonnet-4-20250514")); - AgentV1SettingsAgentContext anthropicConfig = AgentV1SettingsAgentContext.builder() - .think(AgentV1SettingsAgentContextThink.of(anthropicThink)) - .speak(AgentV1SettingsAgentContextSpeak.of(speakSettings)) + AgentV1SettingsAgent anthropicConfig = AgentV1SettingsAgent.builder() + .think(AgentV1SettingsAgentThink.of(List.of(AgentV1SettingsAgentThinkOneItem.builder() + .provider(AgentV1SettingsAgentThinkOneItemProvider.of(anthropicProvider)) + .prompt("You are a helpful assistant powered by Anthropic Claude.") + .build()))) + .speak(speakSettings) .greeting("Hello! I'm powered by Anthropic Claude.") .build(); System.out.println(" Think: Anthropic Claude Sonnet 4"); @@ -76,16 +70,14 @@ public static void main(String[] args) { // Combination 3: Google Gemini + Deepgram System.out.println("=== Combination 3: Google + Deepgram ==="); - Google googleProvider = - Google.builder().model(GoogleThinkProviderModel.GEMINI25FLASH).build(); - ThinkSettingsV1 googleThink = ThinkSettingsV1.builder() - .provider(ThinkSettingsV1Provider.google(googleProvider)) - .prompt("You are a helpful assistant powered by Google Gemini.") - .build(); + Google googleProvider = Google.of(Map.of("model", "gemini-2.5-flash")); - AgentV1SettingsAgentContext googleConfig = AgentV1SettingsAgentContext.builder() - .think(AgentV1SettingsAgentContextThink.of(googleThink)) - .speak(AgentV1SettingsAgentContextSpeak.of(speakSettings)) + AgentV1SettingsAgent googleConfig = AgentV1SettingsAgent.builder() + .think(AgentV1SettingsAgentThink.of(List.of(AgentV1SettingsAgentThinkOneItem.builder() + .provider(AgentV1SettingsAgentThinkOneItemProvider.of(googleProvider)) + .prompt("You are a helpful assistant powered by Google Gemini.") + .build()))) + .speak(speakSettings) .greeting("Hello! I'm powered by Google Gemini.") .build(); System.out.println(" Think: Google Gemini 2.5 Flash"); diff --git a/examples/agent/VoiceAgent.java b/examples/agent/VoiceAgent.java index ce93e988..701bbe67 100644 --- a/examples/agent/VoiceAgent.java +++ b/examples/agent/VoiceAgent.java @@ -1,16 +1,16 @@ import com.deepgram.DeepgramClient; import com.deepgram.resources.agent.v1.types.AgentV1Settings; import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgent; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContext; -import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContextThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThink; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItem; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentThinkOneItemProvider; import com.deepgram.resources.agent.v1.types.AgentV1SettingsAudio; import com.deepgram.resources.agent.v1.websocket.V1WebSocketClient; import com.deepgram.types.OpenAiThinkProvider; -import com.deepgram.types.OpenAiThinkProviderModel; -import com.deepgram.types.ThinkSettingsV1; -import com.deepgram.types.ThinkSettingsV1Provider; import java.io.InputStream; import java.net.URI; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -59,20 +59,15 @@ public static void main(String[] args) { // Send agent settings after receiving welcome try { // Configure the LLM think provider (OpenAI) - OpenAiThinkProvider openAiProvider = OpenAiThinkProvider.builder() - .model(OpenAiThinkProviderModel.GPT4O_MINI) - .build(); - - ThinkSettingsV1 thinkSettings = ThinkSettingsV1.builder() - .provider(ThinkSettingsV1Provider.openAi(openAiProvider)) - .prompt("You are a helpful voice assistant. Keep your responses brief.") - .build(); + OpenAiThinkProvider openAiProvider = OpenAiThinkProvider.of(Map.of("model", "gpt-4o-mini")); - AgentV1SettingsAgentContext agentContext = AgentV1SettingsAgentContext.builder() - .think(AgentV1SettingsAgentContextThink.of(thinkSettings)) + AgentV1SettingsAgent agentConfig = AgentV1SettingsAgent.builder() + .think(AgentV1SettingsAgentThink.of(List.of(AgentV1SettingsAgentThinkOneItem.builder() + .provider(AgentV1SettingsAgentThinkOneItemProvider.of(openAiProvider)) + .prompt("You are a helpful voice assistant. Keep your responses brief.") + .build()))) .greeting("Hello! How can I help you today?") .build(); - AgentV1SettingsAgent agentConfig = AgentV1SettingsAgent.of(agentContext); AgentV1Settings settings = AgentV1Settings.builder() .audio(AgentV1SettingsAudio.builder().build()) diff --git a/src/test/java/com/deepgram/ReadmeSnippetsCompileTest.java b/src/test/java/com/deepgram/ReadmeSnippetsCompileTest.java new file mode 100644 index 00000000..1bf0f669 --- /dev/null +++ b/src/test/java/com/deepgram/ReadmeSnippetsCompileTest.java @@ -0,0 +1,291 @@ +package com.deepgram; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import org.junit.jupiter.api.Test; + +/** Compile-smoke tests for Java snippets embedded in README.md. */ +class ReadmeSnippetsCompileTest { + + private static final Path README_PATH = Paths.get("README.md"); + + private static final List COMMON_IMPORTS = Arrays.asList( + "import com.deepgram.*;", + "import com.deepgram.core.*;", + "import com.deepgram.core.transport.*;", + "import com.deepgram.errors.*;", + "import com.deepgram.resources.agent.v1.types.*;", + "import com.deepgram.resources.agent.v1.websocket.*;", + "import com.deepgram.resources.listen.v1.media.requests.*;", + "import com.deepgram.resources.listen.v1.media.types.*;", + "import com.deepgram.resources.listen.v1.types.*;", + "import com.deepgram.resources.listen.v1.websocket.*;", + "import com.deepgram.resources.read.v1.text.requests.*;", + "import com.deepgram.resources.speak.v1.audio.requests.*;", + "import com.deepgram.resources.speak.v1.types.*;", + "import com.deepgram.resources.speak.v1.websocket.*;", + "import com.deepgram.sagemaker.*;", + "import com.deepgram.types.*;", + "import java.io.*;", + "import java.nio.file.*;", + "import java.util.*;", + "import java.util.concurrent.*;", + "import java.util.function.*;", + "import okio.*;", + "import okhttp3.*;"); + + @Test + void readmeJavaExamplesCompile() throws Exception { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + assertNotNull(compiler, "README snippet compilation requires a JDK, not a JRE"); + + List snippets = extractCompilableJavaSnippets(); + assertFalse(snippets.isEmpty(), "Expected to find Java snippets in README.md"); + + Path tempDir = Files.createTempDirectory("readme-snippets"); + try { + Map snippetsByClassName = new HashMap<>(); + List sourceFiles = new ArrayList<>(); + for (int i = 0; i < snippets.size(); i++) { + String className = String.format("ReadmeSnippet%02d", i + 1); + Snippet snippet = snippets.get(i); + snippetsByClassName.put(className, snippet); + + Path sourceFile = tempDir.resolve(className + ".java"); + Files.writeString(sourceFile, renderSnippetSource(className, snippet), StandardCharsets.UTF_8); + sourceFiles.add(sourceFile); + } + + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + try (StandardJavaFileManager fileManager = + compiler.getStandardFileManager(diagnostics, null, StandardCharsets.UTF_8)) { + Iterable compilationUnits = + fileManager.getJavaFileObjectsFromPaths(sourceFiles); + + List options = Arrays.asList( + "--release", + "11", + "-classpath", + System.getProperty("java.class.path"), + "-d", + tempDir.toString()); + + Boolean success = compiler + .getTask(null, fileManager, diagnostics, options, null, compilationUnits) + .call(); + + if (!Boolean.TRUE.equals(success)) { + fail(formatDiagnostics(diagnostics.getDiagnostics(), snippetsByClassName)); + } + } + } finally { + deleteRecursively(tempDir); + } + } + + private static List extractCompilableJavaSnippets() throws IOException { + List lines = Files.readAllLines(README_PATH, StandardCharsets.UTF_8); + List snippets = new ArrayList<>(); + + String currentH2 = ""; + String currentH3 = ""; + boolean inJavaFence = false; + int snippetStartLine = -1; + StringBuilder snippetBody = new StringBuilder(); + + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + + if (!inJavaFence) { + if (line.startsWith("## ")) { + currentH2 = line.substring(3).trim(); + currentH3 = ""; + } else if (line.startsWith("### ")) { + currentH3 = line.substring(4).trim(); + } + + if (line.equals("```java")) { + inJavaFence = true; + snippetStartLine = i + 1; + snippetBody.setLength(0); + } + continue; + } + + if (line.equals("```")) { + inJavaFence = false; + if (!"Complete SDK Reference".equals(currentH2)) { + String name = currentH3.isEmpty() ? currentH2 : currentH2 + " / " + currentH3; + snippets.add(new Snippet(name, snippetStartLine, snippetBody.toString())); + } + continue; + } + + snippetBody.append(line).append('\n'); + } + + return snippets; + } + + private static String renderSnippetSource(String className, Snippet snippet) { + LinkedHashSet imports = new LinkedHashSet<>(COMMON_IMPORTS); + StringBuilder body = new StringBuilder(); + + for (String line : snippet.code().split("\\R", -1)) { + if (line.startsWith("import ")) { + imports.add(line.endsWith(";") ? line : line + ";"); + } else { + body.append(line).append('\n'); + } + } + + return imports.stream().collect(Collectors.joining("\n")) + + "\n\n" + + "public final class " + + className + + " {\n" + + " private static final DeepgramClient client =\n" + + " DeepgramClient.builder().apiKey(\"test-api-key\").build();\n" + + " private static final AsyncDeepgramClient asyncClient =\n" + + " AsyncDeepgramClient.builder().apiKey(\"test-api-key\").build();\n" + + " private static final ListenV1RequestUrl request = ListenV1RequestUrl.builder()\n" + + " .url(\"https://example.com/audio.wav\")\n" + + " .build();\n" + + " private static final byte[] body = new byte[0];\n" + + "\n" + + " private static final class MyCustomTransport implements DeepgramTransport {\n" + + " private MyCustomTransport(String url, Map headers) {}\n" + + "\n" + + " @Override\n" + + " public CompletableFuture sendBinary(byte[] data) {\n" + + " return CompletableFuture.completedFuture(null);\n" + + " }\n" + + "\n" + + " @Override\n" + + " public CompletableFuture sendText(String data) {\n" + + " return CompletableFuture.completedFuture(null);\n" + + " }\n" + + "\n" + + " @Override\n" + + " public void onTextMessage(Consumer listener) {}\n" + + "\n" + + " @Override\n" + + " public void onBinaryMessage(Consumer listener) {}\n" + + "\n" + + " @Override\n" + + " public void onOpen(Runnable listener) {}\n" + + "\n" + + " @Override\n" + + " public void onError(Consumer listener) {}\n" + + "\n" + + " @Override\n" + + " public void onClose(CloseListener listener) {}\n" + + "\n" + + " @Override\n" + + " public boolean isOpen() {\n" + + " return true;\n" + + " }\n" + + "\n" + + " @Override\n" + + " public void close() {}\n" + + " }\n" + + "\n" + + " public static void compileOnly() throws Exception {\n" + + indent(body.toString(), 8) + + " }\n" + + "}\n"; + } + + private static String formatDiagnostics( + List> diagnostics, Map snippetsByClassName) { + StringBuilder message = new StringBuilder("README Java snippets failed to compile:\n"); + for (Diagnostic diagnostic : diagnostics) { + JavaFileObject source = diagnostic.getSource(); + String className = source == null + ? "unknown" + : Path.of(source.toUri()).getFileName().toString().replace(".java", ""); + Snippet snippet = snippetsByClassName.get(className); + + if (snippet != null) { + message.append("- ") + .append(snippet.name()) + .append(" (README line ") + .append(snippet.startLine()) + .append(")\n"); + } else { + message.append("- ").append(className).append('\n'); + } + + message.append(" ") + .append(diagnostic.getKind()) + .append(": line ") + .append(diagnostic.getLineNumber()) + .append(": ") + .append(diagnostic.getMessage(null)) + .append('\n'); + } + return message.toString(); + } + + private static String indent(String text, int spaces) { + String prefix = " ".repeat(spaces); + return Arrays.stream(text.split("\\R", -1)) + .map(line -> line.isEmpty() ? "" : prefix + line) + .collect(Collectors.joining("\n", "", "\n")); + } + + private static void deleteRecursively(Path path) throws IOException { + if (path == null || !Files.exists(path)) { + return; + } + try (java.util.stream.Stream stream = Files.walk(path)) { + for (Path current : stream.sorted((a, b) -> b.compareTo(a)).collect(Collectors.toList())) { + Files.deleteIfExists(current); + } + } + } + + private static final class Snippet { + private final String name; + private final int startLine; + private final String code; + + private Snippet(String name, int startLine, String code) { + this.name = name; + this.startLine = startLine; + this.code = code; + } + + private String name() { + return name; + } + + private int startLine() { + return startLine; + } + + private String code() { + return code; + } + } +}