Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
92b6657
feat: add audio transcription interface
hanzeINGH Feb 26, 2025
1311529
save chat ws
hanzeINGH Feb 28, 2025
c4dfaf7
save ws transcription
hanzeINGH Mar 3, 2025
bd58521
format code
hanzeINGH Mar 3, 2025
8ae410b
add ut
hanzeINGH Mar 3, 2025
b562d4d
format code
hanzeINGH Mar 3, 2025
99910cf
format code
hanzeINGH Mar 3, 2025
75c382a
fix ut
hanzeINGH Mar 3, 2025
f35b3c2
fix ci
hanzeINGH Mar 3, 2025
b57805e
fix ci
hanzeINGH Mar 3, 2025
7315347
fix ci
hanzeINGH Mar 3, 2025
65e3689
fix ci
hanzeINGH Mar 3, 2025
4e85b6f
fix ci
hanzeINGH Mar 3, 2025
c5ed443
fix ci
hanzeINGH Mar 3, 2025
d8835b2
fix ci
hanzeINGH Mar 3, 2025
7f51524
fix ci
hanzeINGH Mar 3, 2025
43be7ee
fix ci
hanzeINGH Mar 3, 2025
1fdab4d
fix ci
hanzeINGH Mar 3, 2025
be44ab1
fix ci
hanzeINGH Mar 3, 2025
4fcadd3
fix ci
hanzeINGH Mar 3, 2025
7ee1a3e
fix ci
hanzeINGH Mar 3, 2025
b1294a5
fix ci
hanzeINGH Mar 3, 2025
9b400d7
fix ci
hanzeINGH Mar 3, 2025
51f1f1e
fix ci
hanzeINGH Mar 3, 2025
680a6eb
fix ci
hanzeINGH Mar 3, 2025
159abbe
fix ci
hanzeINGH Mar 3, 2025
1d592b8
fix ci
hanzeINGH Mar 3, 2025
931d813
fix ci
hanzeINGH Mar 3, 2025
f9ce9d4
fix ci
hanzeINGH Mar 3, 2025
143248f
fix ci
hanzeINGH Mar 3, 2025
dc60f14
fix ci
hanzeINGH Mar 3, 2025
ef75f9c
fix ci
hanzeINGH Mar 3, 2025
c69b6a4
fix ci
hanzeINGH Mar 4, 2025
c28e365
fix ci
hanzeINGH Mar 5, 2025
2cc6153
fix cr
hanzeINGH Mar 5, 2025
8f12072
fix code format
hanzeINGH Mar 5, 2025
f3f5e9f
fix: cr
hanzeINGH Mar 5, 2025
7b745dd
fix: code format
hanzeINGH Mar 5, 2025
92f47cb
fix cr
hanzeINGH Mar 5, 2025
34793a8
fix code style
hanzeINGH Mar 5, 2025
a1f71cd
fix ci
hanzeINGH Mar 5, 2025
74bf190
jwt refresh timing
hanzeINGH Mar 5, 2025
5e60949
jwt refresh timing
hanzeINGH Mar 5, 2025
bac158f
fix cr
hanzeINGH Mar 10, 2025
e0dc61d
fix cr
hanzeINGH Mar 10, 2025
5da6135
fix cr
hanzeINGH Mar 10, 2025
b0359ab
fix cr
hanzeINGH Mar 10, 2025
a2d2018
add ut
hanzeINGH Mar 10, 2025
d2ae3ab
fix error code
hanzeINGH Mar 11, 2025
30a8a9c
add: turn detection event
hanzeINGH Mar 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,47 @@ jobs:
strategy:
fail-fast: false
matrix:
java-version: [ "8", "11", "17" ]
os: [ "ubuntu-latest" ]
os-label: [ "Ubuntu" ]
java-version: [ "11", "17" ]
os: [ "ubuntu-latest", "windows-latest", "macos-latest" ]
os-label: [ "Ubuntu", "Windows", "macOS" ]
include:
- { java-version: "11", os: "windows-latest", os-label: "Windows" }
- { java-version: "11", os: "macos-latest", os-label: "macOS" }
- { java-version: "8", os: "macos-latest", os-label: "macOS", distribution: "zulu" }
exclude:
- os: "windows-latest"
os-label: "Ubuntu"
- os: "windows-latest"
os-label: "macOS"
- os: "macos-latest"
os-label: "Ubuntu"
- os: "macos-latest"
os-label: "Windows"
- os: "ubuntu-latest"
os-label: "Windows"
- os: "ubuntu-latest"
os-label: "macOS"
steps:
- uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v4
with:
distribution: 'adopt'
distribution: ${{ matrix.java-version == '8' && matrix.os == 'macos-latest' && 'zulu' || 'temurin' }}
java-version: ${{ matrix.java-version }}
cache: 'maven'
java-package: 'jdk'
- name: Set JAVA_HOME (Windows)
if: runner.os == 'Windows'
run: |
echo "JAVA_HOME=${{ env.JAVA_HOME }}" >> $GITHUB_ENV
echo "${{ env.JAVA_HOME }}/bin" >> $GITHUB_PATH
shell: bash
- name: Code style check
run: mvn spotless:check
run: |
echo "Java version:"
java -version
echo "Javac version:"
javac -version
mvn -v
mvn spotless:check
- name: Build and Test with Coverage
run: |
mvn -pl api clean test-compile
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
**/bin/*
**/obj/*
.mvn
*.wav

# Compiled class file
*.class
Expand Down
122 changes: 122 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ dependencies {
| how to handle exception | [HandlerExceptionExample.java](example/src/main/java/example/service/HandlerExceptionExample.java) |
| get request log id | [GetLogExample.java](example/src/main/java/example/service/GetLogExample.java) |
| set timeout | [SetRequestTimeoutExample.java](example/src/main/java/example/service/SetRequestTimeoutExample.java) |
| websocket chat | [ChatExample.java](example/src/main/java/example/websocket/chat/ChatExample.java) |
| websocket speech synthesis | [WebsocketAudioSpeechExample.java](example/src/main/java/example/websocket/audio/speech/WebsocketAudioSpeechExample.java) |
| websocket transcription | [WebsocketTranscriptionsExample.java](example/src/main/java/example/websocket/audio/transcriptions/WebsocketTranscriptionsExample.java) |

### Initialize the Coze Client

Expand Down Expand Up @@ -984,4 +987,123 @@ while (iterator.hasNext()) {

```

### WebSocket

The SDK provides WebSocket interfaces for real-time chat, speech synthesis and speech transcription.

You can check the official documentation for more information:
https://www.coze.cn/open/docs/guides/websocket_openapi

#### WebSocket Chat

WebSocket chat allows real-time communication with bots, including text and audio interactions:

```java
WebsocketChatClient client = coze.websocket()
.chat()
.create(new WebsocketChatCreateReq(botID, new CallbackHandler()));

// Send audio data
String audioData = "..."; // Base64 encoded audio data
client.inputAudioBufferAppend(audioData);
client.inputAudioBufferComplete();

// Handle responses in callback
class CallbackHandler extends WebsocketChatCallbackHandler {
// Handle text responses
@Override
public void onConversationMessageDelta(WebsocketChatClient client, ConversationMessageDeltaEvent event) {
System.out.printf("Received: %s\n", event.getData().getContent());
}

// Handle audio responses
@Override
public void onConversationAudioDelta(WebsocketChatClient client, ConversationAudioDeltaEvent event) {
byte[] audioData = event.getData().getAudio();
// Process audio data...
}
}
```

#### Speech Synthesis

WebSocket speech synthesis allows real-time text-to-speech conversion:

```java
WebsocketAudioSpeechClient client = coze.websocket()
.audio()
.speech()
.create(new WebsocketAudioSpeechCreateReq(new CallbackHandler()));

// Configure audio output
OutputAudio outputAudio = OutputAudio.builder()
.voiceId(voiceID)
.codec("pcm")
.speechRate(50)
.pcmConfig(PCMConfig.builder().sampleRate(24000).build())
.build();
client.speechUpdate(new SpeechUpdateEventData(outputAudio));

// Send text for synthesis
client.inputTextBufferAppend("Hello world!");
client.inputTextBufferComplete();

// Handle synthesized audio in callback
class CallbackHandler extends WebsocketAudioSpeechCallbackHandler {
@Override
public void onSpeechAudioUpdate(WebsocketAudioSpeechClient client, SpeechAudioUpdateEvent event) {
byte[] audioData = event.getDelta();
// Process audio data...
}
}
```

#### Speech Transcription

WebSocket speech transcription provides real-time speech-to-text conversion:

```java
WebsocketAudioTranscriptionsClient client = coze.websocket()
.audio()
.transcriptions()
.create(new WebsocketAudioTranscriptionsCreateReq(new CallbackHandler()));

// Configure audio input
InputAudio inputAudio = InputAudio.builder()
.sampleRate(24000)
.codec("pcm")
.format("wav")
.channel(2)
.build();
client.transcriptionsUpdate(new TranscriptionsUpdateEventData(inputAudio));

// Send audio for transcription
String audioData = "..."; // Base64 encoded audio data
client.inputAudioBufferAppend(audioData);
client.inputAudioBufferComplete();

// Handle transcription results in callback
class CallbackHandler extends WebsocketAudioTranscriptionsCallbackHandler {
@Override
public void onTranscriptionsMessageUpdate(
WebsocketAudioTranscriptionsClient client,
TranscriptionsMessageUpdateEvent event) {
System.out.println(event.getData().getContent());
}
}
```

All WebSocket clients support proper resource cleanup:

```java
try {
// Use the client...
} finally {
if (client != null) {
client.close();
}
coze.shutdownExecutor();
}
```


13 changes: 8 additions & 5 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
</parent>

<artifactId>coze-api</artifactId>
<version>0.2.8</version>
<version>0.2.9</version>

<scm>
<connection>scm:git:git://github.com/coze-dev/coze-java.git</connection>
Expand All @@ -68,7 +68,7 @@
<jackson.version>2.14.2</jackson.version>
<jwt.version>0.11.5</jwt.version>
<junit.version>5.10.2</junit.version>
<okhttp.version>4.8.1</okhttp.version>
<okhttp.version>3.14.9</okhttp.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -280,17 +280,20 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<version>3.2.5</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<useSystemClassLoader>false</useSystemClassLoader>
<forkCount>1</forkCount>
<reuseForks>true</reuseForks>
<argLine>@{argLine} -Djava.security.egd=file:/dev/./urandom</argLine>
<argLine>@{argLine}</argLine>
<systemPropertyVariables>
<okhttp.platform>jdk</okhttp.platform>
<okhttp.platform>jdk-8</okhttp.platform>
<java.security.egd>file:/dev/urandom</java.security.egd>
<javax.net.ssl.trustStore>${java.home}/lib/security/cacerts</javax.net.ssl.trustStore>
<javax.net.ssl.trustStorePassword>changeit</javax.net.ssl.trustStorePassword>
</systemPropertyVariables>
</configuration>
</plugin>
Expand Down
19 changes: 19 additions & 0 deletions api/src/main/java/com/coze/openapi/api/AudioTranscriptionAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.coze.openapi.api;

import com.coze.openapi.client.audio.transcriptions.CreateTranscriptionsResp;
import com.coze.openapi.client.common.BaseReq;
import com.coze.openapi.client.common.BaseResponse;

import okhttp3.MultipartBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Tag;

public interface AudioTranscriptionAPI {
@Multipart
@POST("/v1/audio/transcriptions")
Call<BaseResponse<CreateTranscriptionsResp>> create(
@Part MultipartBody.Part file, @Tag BaseReq baseReq);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ public class CreateSpeechReq extends BaseReq {
@JsonProperty("speed")
@Builder.Default
private float speed = 1.0f;

@JsonProperty("sample_rate")
private int sampleRate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.coze.openapi.client.audio.transcriptions;

import java.io.File;

import com.coze.openapi.client.common.BaseReq;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Getter
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CreateTranscriptionsReq extends BaseReq {
/** local file path */
private String filePath;

/*
* file byte array
*/
private byte[] fileBytes;

/** file name */
private String fileName;

/** file object */
private File file;

public static CreateTranscriptionsReq of(String fileName, byte[] fileBytes) {
return CreateTranscriptionsReq.builder().fileName(fileName).fileBytes(fileBytes).build();
}

public static CreateTranscriptionsReq of(File file) {
return CreateTranscriptionsReq.builder().file(file).build();
}

public static CreateTranscriptionsReq of(String filePath) {
return CreateTranscriptionsReq.builder().filePath(filePath).build();
}
}
Comment on lines +1 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement validation for mutually exclusive file source fields.

The class accepts multiple ways to provide file data (filePath, fileBytes, file object) without validation logic to ensure that at least one valid file source is provided or to handle cases when multiple sources are specified.

Consider adding a validation method that:

  1. Ensures at least one file source is specified
  2. Defines a clear precedence when multiple sources are provided
  3. Validates file formats or sizes if applicable

Here's a suggested implementation:

@Getter
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CreateTranscriptionsReq extends BaseReq {
  /** local file path */
  private String filePath;

  /** file byte array */
  private byte[] fileBytes;

  /** file name */
  private String fileName;

  /** file object */
  private File file;

+  /**
+   * Validates that this request has at least one valid file source.
+   * @return true if the request has valid file data
+   * @throws IllegalArgumentException if no valid file source is provided
+   */
+  public boolean validate() {
+    if (file != null && file.exists() && file.isFile()) {
+      return true;
+    }
+    if (filePath != null && !filePath.isEmpty()) {
+      return true;
+    }
+    if (fileBytes != null && fileBytes.length > 0 && fileName != null && !fileName.isEmpty()) {
+      return true;
+    }
+    throw new IllegalArgumentException("No valid file source provided. Please provide either a file path, file object, or file bytes with a name.");
+  }

  public static CreateTranscriptionsReq of(String fileName, byte[] fileBytes) {
    return CreateTranscriptionsReq.builder().fileName(fileName).fileBytes(fileBytes).build();
  }

  public static CreateTranscriptionsReq of(File file) {
    return CreateTranscriptionsReq.builder().file(file).build();
  }

  public static CreateTranscriptionsReq of(String filePath) {
    return CreateTranscriptionsReq.builder().filePath(filePath).build();
  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
package com.coze.openapi.client.audio.transcriptions;
import java.io.File;
import com.coze.openapi.client.common.BaseReq;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Getter
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CreateTranscriptionsReq extends BaseReq {
/** local file path */
private String filePath;
/*
* file byte array
*/
private byte[] fileBytes;
/** file name */
private String fileName;
/** file object */
private File file;
public static CreateTranscriptionsReq of(String fileName, byte[] fileBytes) {
return CreateTranscriptionsReq.builder().fileName(fileName).fileBytes(fileBytes).build();
}
public static CreateTranscriptionsReq of(File file) {
return CreateTranscriptionsReq.builder().file(file).build();
}
public static CreateTranscriptionsReq of(String filePath) {
return CreateTranscriptionsReq.builder().filePath(filePath).build();
}
}
package com.coze.openapi.client.audio.transcriptions;
import java.io.File;
import com.coze.openapi.client.common.BaseReq;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Getter
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CreateTranscriptionsReq extends BaseReq {
/** local file path */
private String filePath;
/** file byte array */
private byte[] fileBytes;
/** file name */
private String fileName;
/** file object */
private File file;
/**
* Validates that this request has at least one valid file source.
* @return true if the request has valid file data
* @throws IllegalArgumentException if no valid file source is provided
*/
public boolean validate() {
if (file != null && file.exists() && file.isFile()) {
return true;
}
if (filePath != null && !filePath.isEmpty()) {
return true;
}
if (fileBytes != null && fileBytes.length > 0 && fileName != null && !fileName.isEmpty()) {
return true;
}
throw new IllegalArgumentException("No valid file source provided. Please provide either a file path, file object, or file bytes with a name.");
}
public static CreateTranscriptionsReq of(String fileName, byte[] fileBytes) {
return CreateTranscriptionsReq.builder().fileName(fileName).fileBytes(fileBytes).build();
}
public static CreateTranscriptionsReq of(File file) {
return CreateTranscriptionsReq.builder().file(file).build();
}
public static CreateTranscriptionsReq of(String filePath) {
return CreateTranscriptionsReq.builder().filePath(filePath).build();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.coze.openapi.client.audio.transcriptions;

import com.coze.openapi.client.common.BaseResp;
import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.*;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CreateTranscriptionsResp extends BaseResp {
@JsonProperty("text")
private String text;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.coze.openapi.client.bots;

import org.jetbrains.annotations.NotNull;

import com.coze.openapi.client.bots.model.BotOnboardingInfo;
import com.coze.openapi.client.bots.model.BotPromptInfo;
import com.coze.openapi.client.common.BaseReq;
Expand All @@ -11,6 +9,7 @@
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.experimental.SuperBuilder;

@Data
Expand All @@ -19,11 +18,11 @@
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CreateBotReq extends BaseReq {
@NotNull
@NonNull
@JsonProperty("space_id")
String spaceID;

@NotNull
@NonNull
@JsonProperty("name")
String name;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package com.coze.openapi.client.bots;

import org.jetbrains.annotations.NotNull;

import com.coze.openapi.client.bots.model.BotKnowledge;
import com.coze.openapi.client.bots.model.BotOnboardingInfo;
import com.coze.openapi.client.bots.model.BotPromptInfo;
import com.coze.openapi.client.common.BaseReq;
import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.*;
import lombok.experimental.SuperBuilder;

@Data
Expand All @@ -21,7 +16,7 @@
@EqualsAndHashCode(callSuper = true)
public class UpdateBotReq extends BaseReq {

@NotNull
@NonNull
@JsonProperty("bot_id")
private String botID;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public class ChatUsage {
@JsonProperty("output_count")
private int outputCount;

@JsonProperty("output_tokens")
private int outputTokens;

@JsonProperty("input_tokens")
private int inputTokens;

/** The total number of Tokens consumed for the input part. */
@JsonProperty("input_count")
private int inputCount;
Expand Down
Loading