Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ First, install the SDK in your project
npm install @anam-ai/js-sdk
```

## Deprecation Notice

**Important**: The `brainType` field in `PersonaConfig` is deprecated and will be removed in a future version. Please use `llmId` instead. If you are currently using `brainType`, you will see a deprecation warning in the console. Both fields are supported during the transition period.

## Local development

The quickest way to start testing the SDK is to use your API key directly with our SDK and the example persona config shown below.
Expand Down Expand Up @@ -84,7 +88,7 @@ const response = await fetch(`https://api.anam.ai/v1/auth/session-token`, {
name: 'Cara',
avatarId: '30fa96d0-26c4-4e55-94a0-517025942e18',
voiceId: '6bfbe25a-979d-40f3-a92b-5394170af54b',
brainType: 'ANAM_GPT_4O_MINI_V1',
llmId: '<LLM ID HERE>',
systemPrompt:
"[STYLE] Reply in natural speech without formatting. Add pauses using '...' and very occasionally a disfluency. [PERSONALITY] You are Cara, a helpful assistant.",
},
Expand Down
47 changes: 43 additions & 4 deletions src/AnamClient.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Buffer } from 'buffer';
import { ClientError, ErrorCode } from './lib/ClientError';
import { generateCorrelationId } from './lib/correlationId';
import {
ClientMetricMeasurement,
DEFAULT_ANAM_API_VERSION,
DEFAULT_ANAM_METRICS_BASE_URL,
setClientMetricsBaseUrl,
sendClientMetric,
setClientMetricsBaseUrl,
setMetricsContext,
} from './lib/ClientMetrics';
import { generateCorrelationId } from './lib/correlationId';
import {
CoreApiRestClient,
InternalEventEmitter,
Expand All @@ -18,15 +19,14 @@ import {
import {
AnamClientOptions,
AnamEvent,
ConnectionClosedCode,
EventCallbacks,
InputAudioState,
PersonaConfig,
StartSessionOptions,
StartSessionResponse,
ConnectionClosedCode,
} from './types';
import { TalkMessageStream } from './types/TalkMessageStream';
import { Buffer } from 'buffer';
export default class AnamClient {
private publicEventEmitter: PublicEventEmitter;
private internalEventEmitter: InternalEventEmitter;
Expand Down Expand Up @@ -386,6 +386,11 @@ export default class AnamClient {
this.streamingClient.startConnection();
}

/**
* Send a talk command to make the persona speak the provided content.
* @param content - The text content for the persona to speak
* @throws Error if session is not started or not currently streaming
*/
public async talk(content: string): Promise<void> {
if (!this.streamingClient) {
throw new Error(
Expand All @@ -401,6 +406,11 @@ export default class AnamClient {
return;
}

/**
* Send a raw data message through the WebRTC data channel.
* @param message - The message string to send through the data channel
* @throws Error if session is not started
*/
public sendDataMessage(message: string): void {
if (this.streamingClient) {
this.streamingClient.sendDataMessage(message);
Expand All @@ -409,6 +419,35 @@ export default class AnamClient {
}
}

/**
* Send a user text message in the active streaming session.
* @param content - The text message content to send
* @throws Error if not currently streaming or session is not started
*/
public sendUserMessage(content: string): void {
if (!this._isStreaming) {
console.warn(
'AnamClient: Not currently streaming. User message will not be sent.',
);
throw new Error('Failed to send user message: not currently streaming');
}

const sessionId = this.getActiveSessionId();
if (!sessionId) {
throw new Error('Failed to send user message: no active session');
}

const currentTimestamp = new Date().toISOString().replace('Z', '');
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

The timestamp format removes the 'Z' suffix but doesn't replace it with anything, resulting in an incomplete ISO timestamp. This could cause issues with timestamp parsing. Consider using a proper timezone offset or keeping the 'Z' for UTC.

Suggested change
const currentTimestamp = new Date().toISOString().replace('Z', '');
const currentTimestamp = new Date().toISOString();

Copilot uses AI. Check for mistakes.
const body = JSON.stringify({
content,
timestamp: currentTimestamp,
session_id: sessionId,
message_type: 'speech',
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

The hardcoded 'speech' message type as a magic string should be extracted to a constant or enum to improve maintainability and prevent typos.

Suggested change
message_type: 'speech',
message_type: MESSAGE_TYPE_SPEECH,

Copilot uses AI. Check for mistakes.
});

this.sendDataMessage(body);
}

public async stopStreaming(): Promise<void> {
if (this.streamingClient) {
this.publicEventEmitter.emit(
Expand Down
15 changes: 15 additions & 0 deletions src/modules/CoreApiRestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export class CoreApiRestClient {
this.sessionToken = await this.unsafe_getSessionToken(personaConfig);
}

// Check if brainType is being used and log deprecation warning
if (personaConfig && 'brainType' in personaConfig) {
console.warn(
'Warning: brainType is deprecated and will be removed in a future version. Please use llmId instead.',
);
}

try {
const response = await fetch(`${this.getApiUrl()}/engine/session`, {
method: 'POST',
Expand Down Expand Up @@ -139,6 +146,14 @@ export class CoreApiRestClient {
if (!this.apiKey) {
throw new Error('No apiKey provided');
}

// Check if brainType is being used and log deprecation warning
if (personaConfig && 'brainType' in personaConfig) {
console.warn(
'Warning: brainType is deprecated and will be removed in a future version. Please use llmId instead.',
);
}

let body: { clientLabel: string; personaConfig?: PersonaConfig } = {
clientLabel: 'js-sdk-api-key',
};
Expand Down
4 changes: 2 additions & 2 deletions src/types/PersonaConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface CustomPersonaConfig {
name: string;
avatarId: string;
voiceId: string;
brainType: string;
llmId?: string;
systemPrompt?: string;
maxSessionLengthSeconds?: number;
languageCode?: string;
Expand All @@ -14,5 +14,5 @@ export interface CustomPersonaConfig {
export function isCustomPersonaConfig(
personaConfig: PersonaConfig,
): personaConfig is CustomPersonaConfig {
return 'brainType' in personaConfig;
return 'brainType' in personaConfig || 'llmId' in personaConfig;
}