Skip to content

Commit 5bf13c8

Browse files
authored
Merge branch 'elizaOS:develop' into develop
2 parents 80126d8 + 6490485 commit 5bf13c8

File tree

3 files changed

+121
-66
lines changed

3 files changed

+121
-66
lines changed

packages/client-twitter/src/plugins/SttTtsSpacesPlugin.ts

+33-35
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class SttTtsPlugin implements Plugin {
101101

102102
init(params: { space: Space; pluginConfig?: Record<string, any> }): void {
103103
elizaLogger.log(
104-
"[SttTtsPlugin] init => Space fully ready. Subscribing to events."
104+
"[SttTtsPlugin] init => Space fully ready. Subscribing to events.",
105105
);
106106

107107
this.space = params.space;
@@ -127,7 +127,6 @@ export class SttTtsPlugin implements Plugin {
127127
if (config?.chatContext) {
128128
this.chatContext = config.chatContext;
129129
}
130-
elizaLogger.log("[SttTtsPlugin] Plugin config =>", config);
131130

132131
this.volumeBuffers = new Map<string, number[]>();
133132
}
@@ -163,14 +162,14 @@ export class SttTtsPlugin implements Plugin {
163162
this.userSpeakingTimer = setTimeout(() => {
164163
elizaLogger.log(
165164
"[SttTtsPlugin] start processing audio for user =>",
166-
data.userId
165+
data.userId,
167166
);
168167
this.userSpeakingTimer = null;
169168
this.processAudio(data.userId).catch((err) =>
170169
elizaLogger.error(
171170
"[SttTtsPlugin] handleSilence error =>",
172-
err
173-
)
171+
err,
172+
),
174173
);
175174
}, SILENCE_DETECTION_THRESHOLD_MS);
176175
} else {
@@ -183,7 +182,7 @@ export class SttTtsPlugin implements Plugin {
183182
const samples = new Int16Array(
184183
data.samples.buffer,
185184
data.samples.byteOffset,
186-
data.samples.length / 2
185+
data.samples.length / 2,
187186
);
188187
const maxAmplitude = Math.max(...samples.map(Math.abs)) / 32768;
189188
volumeBuffer.push(maxAmplitude);
@@ -209,7 +208,7 @@ export class SttTtsPlugin implements Plugin {
209208
// /src/sttTtsPlugin.ts
210209
private async convertPcmToWavInMemory(
211210
pcmData: Int16Array,
212-
sampleRate: number
211+
sampleRate: number,
213212
): Promise<ArrayBuffer> {
214213
// number of channels
215214
const numChannels = 1;
@@ -268,20 +267,20 @@ export class SttTtsPlugin implements Plugin {
268267
try {
269268
elizaLogger.log(
270269
"[SttTtsPlugin] Starting audio processing for user:",
271-
userId
270+
userId,
272271
);
273272
const chunks = this.pcmBuffers.get(userId) || [];
274273
this.pcmBuffers.clear();
275274

276275
if (!chunks.length) {
277276
elizaLogger.warn(
278277
"[SttTtsPlugin] No audio chunks for user =>",
279-
userId
278+
userId,
280279
);
281280
return;
282281
}
283282
elizaLogger.log(
284-
`[SttTtsPlugin] Flushing STT buffer for user=${userId}, chunks=${chunks.length}`
283+
`[SttTtsPlugin] Flushing STT buffer for user=${userId}, chunks=${chunks.length}`,
285284
);
286285

287286
const totalLen = chunks.reduce((acc, c) => acc + c.length, 0);
@@ -296,36 +295,35 @@ export class SttTtsPlugin implements Plugin {
296295
const wavBuffer = await this.convertPcmToWavInMemory(merged, 48000);
297296

298297
// Whisper STT
299-
const sttText = await this.transcriptionService.transcribe(
300-
wavBuffer
301-
);
298+
const sttText =
299+
await this.transcriptionService.transcribe(wavBuffer);
302300

303301
elizaLogger.log(
304-
`[SttTtsPlugin] Transcription result: "${sttText}"`
302+
`[SttTtsPlugin] Transcription result: "${sttText}"`,
305303
);
306304

307305
if (!sttText || !sttText.trim()) {
308306
elizaLogger.warn(
309307
"[SttTtsPlugin] No speech recognized for user =>",
310-
userId
308+
userId,
311309
);
312310
return;
313311
}
314312
elizaLogger.log(
315-
`[SttTtsPlugin] STT => user=${userId}, text="${sttText}"`
313+
`[SttTtsPlugin] STT => user=${userId}, text="${sttText}"`,
316314
);
317315

318316
// Get response
319317
const replyText = await this.handleUserMessage(sttText, userId);
320318
if (!replyText || !replyText.length || !replyText.trim()) {
321319
elizaLogger.warn(
322320
"[SttTtsPlugin] No replyText for user =>",
323-
userId
321+
userId,
324322
);
325323
return;
326324
}
327325
elizaLogger.log(
328-
`[SttTtsPlugin] user=${userId}, reply="${replyText}"`
326+
`[SttTtsPlugin] user=${userId}, reply="${replyText}"`,
329327
);
330328
this.isProcessingAudio = false;
331329
this.volumeBuffers.clear();
@@ -348,7 +346,7 @@ export class SttTtsPlugin implements Plugin {
348346
this.processTtsQueue().catch((err) => {
349347
elizaLogger.error(
350348
"[SttTtsPlugin] processTtsQueue error =>",
351-
err
349+
err,
352350
);
353351
});
354352
}
@@ -370,14 +368,14 @@ export class SttTtsPlugin implements Plugin {
370368
const pcm = await this.convertMp3ToPcm(ttsAudio, 48000);
371369
if (signal.aborted) {
372370
elizaLogger.log(
373-
"[SttTtsPlugin] TTS interrupted before streaming"
371+
"[SttTtsPlugin] TTS interrupted before streaming",
374372
);
375373
return;
376374
}
377375
await this.streamToJanus(pcm, 48000);
378376
if (signal.aborted) {
379377
elizaLogger.log(
380-
"[SttTtsPlugin] TTS interrupted after streaming"
378+
"[SttTtsPlugin] TTS interrupted after streaming",
381379
);
382380
return;
383381
}
@@ -396,7 +394,7 @@ export class SttTtsPlugin implements Plugin {
396394
*/
397395
private async handleUserMessage(
398396
userText: string,
399-
userId: string // This is the raw Twitter user ID like 'tw-1865462035586142208'
397+
userId: string, // This is the raw Twitter user ID like 'tw-1865462035586142208'
400398
): Promise<string> {
401399
// Extract the numeric ID part
402400
const numericId = userId.replace("tw-", "");
@@ -410,7 +408,7 @@ export class SttTtsPlugin implements Plugin {
410408
userUuid,
411409
userId, // Use full Twitter ID as username
412410
`Twitter User ${numericId}`,
413-
"twitter"
411+
"twitter",
414412
);
415413

416414
// Ensure room exists and user is in it
@@ -427,7 +425,7 @@ export class SttTtsPlugin implements Plugin {
427425
{
428426
twitterUserName: this.client.profile.username,
429427
agentName: this.runtime.character.name,
430-
}
428+
},
431429
);
432430

433431
const memory = {
@@ -492,7 +490,7 @@ export class SttTtsPlugin implements Plugin {
492490

493491
private async _generateResponse(
494492
message: Memory,
495-
context: string
493+
context: string,
496494
): Promise<Content> {
497495
const { userId, roomId } = message;
498496

@@ -506,7 +504,7 @@ export class SttTtsPlugin implements Plugin {
506504

507505
if (!response) {
508506
elizaLogger.error(
509-
"[SttTtsPlugin] No response from generateMessageResponse"
507+
"[SttTtsPlugin] No response from generateMessageResponse",
510508
);
511509
return;
512510
}
@@ -554,7 +552,7 @@ export class SttTtsPlugin implements Plugin {
554552
if (
555553
(message.content as Content).text.length < 50 &&
556554
loseInterestWords.some((word) =>
557-
(message.content as Content).text?.toLowerCase().includes(word)
555+
(message.content as Content).text?.toLowerCase().includes(word),
558556
)
559557
) {
560558
return true;
@@ -564,7 +562,7 @@ export class SttTtsPlugin implements Plugin {
564562
if (
565563
(message.content as Content).text?.length < 8 &&
566564
ignoreWords.some((word) =>
567-
(message.content as Content).text?.toLowerCase().includes(word)
565+
(message.content as Content).text?.toLowerCase().includes(word),
568566
)
569567
) {
570568
return true;
@@ -575,7 +573,7 @@ export class SttTtsPlugin implements Plugin {
575573

576574
private async _shouldRespond(
577575
message: string,
578-
state: State
576+
state: State,
579577
): Promise<boolean> {
580578
const lowerMessage = message.toLowerCase();
581579
const characterName = this.runtime.character.name.toLowerCase();
@@ -610,7 +608,7 @@ export class SttTtsPlugin implements Plugin {
610608

611609
elizaLogger.error(
612610
"Invalid response from response generateText:",
613-
response
611+
response,
614612
);
615613
return false;
616614
}
@@ -638,7 +636,7 @@ export class SttTtsPlugin implements Plugin {
638636
if (!resp.ok) {
639637
const errText = await resp.text();
640638
throw new Error(
641-
`[SttTtsPlugin] ElevenLabs TTS error => ${resp.status} ${errText}`
639+
`[SttTtsPlugin] ElevenLabs TTS error => ${resp.status} ${errText}`,
642640
);
643641
}
644642
const arrayBuf = await resp.arrayBuffer();
@@ -650,7 +648,7 @@ export class SttTtsPlugin implements Plugin {
650648
*/
651649
private convertMp3ToPcm(
652650
mp3Buf: Buffer,
653-
outRate: number
651+
outRate: number,
654652
): Promise<Int16Array> {
655653
return new Promise((resolve, reject) => {
656654
const ff = spawn("ffmpeg", [
@@ -680,7 +678,7 @@ export class SttTtsPlugin implements Plugin {
680678
const samples = new Int16Array(
681679
raw.buffer,
682680
raw.byteOffset,
683-
raw.byteLength / 2
681+
raw.byteLength / 2,
684682
);
685683
resolve(samples);
686684
});
@@ -696,7 +694,7 @@ export class SttTtsPlugin implements Plugin {
696694
*/
697695
private async streamToJanus(
698696
samples: Int16Array,
699-
sampleRate: number
697+
sampleRate: number,
700698
): Promise<void> {
701699
// TODO: Check if better than 480 fixed
702700
const FRAME_SIZE = Math.floor(sampleRate * 0.01); // 10ms frames => 480 @48kHz
@@ -726,7 +724,7 @@ export class SttTtsPlugin implements Plugin {
726724
public addMessage(role: "system" | "user" | "assistant", content: string) {
727725
this.chatContext.push({ role, content });
728726
elizaLogger.log(
729-
`[SttTtsPlugin] addMessage => role=${role}, content=${content}`
727+
`[SttTtsPlugin] addMessage => role=${role}, content=${content}`,
730728
);
731729
}
732730

packages/client-twitter/src/post.ts

+40-27
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
type UUID,
1111
truncateToCompleteSentence,
1212
parseJSONObjectFromText,
13+
extractAttributes,
14+
cleanJsonResponse,
1315
} from "@elizaos/core";
1416
import { elizaLogger } from "@elizaos/core";
1517
import type { ClientBase } from "./base.ts";
@@ -512,11 +514,7 @@ export class TwitterPostClient {
512514
modelClass: ModelClass.SMALL,
513515
});
514516

515-
const newTweetContent = response
516-
.replace(/```json\s*/g, "") // Remove ```json
517-
.replace(/```\s*/g, "") // Remove any remaining ```
518-
.replace(/(\r\n|\n|\r)/g, "") // Remove line break
519-
.trim();
517+
const newTweetContent = cleanJsonResponse(response);
520518

521519
// First attempt to clean content
522520
let cleanedContent = "";
@@ -530,6 +528,10 @@ export class TwitterPostClient {
530528
cleanedContent = parsedResponse;
531529
}
532530
} catch (error) {
531+
elizaLogger.error(
532+
"Response is not JSON, treating as plain text",
533+
response,
534+
);
533535
error.linted = true; // make linter happy since catch needs a variable
534536
// If not JSON, clean the raw content
535537
cleanedContent = newTweetContent
@@ -540,6 +542,13 @@ export class TwitterPostClient {
540542
.trim();
541543
}
542544

545+
if (!cleanedContent) {
546+
cleanedContent = truncateToCompleteSentence(
547+
extractAttributes(newTweetContent, ["text"]).text,
548+
this.client.twitterConfig.MAX_TWEET_LENGTH,
549+
);
550+
}
551+
543552
if (!cleanedContent) {
544553
elizaLogger.error(
545554
"Failed to extract valid content from response:",
@@ -630,51 +639,55 @@ export class TwitterPostClient {
630639
elizaLogger.log("generate tweet content response:\n" + response);
631640

632641
// First clean up any markdown and newlines
633-
const cleanedResponse = response
634-
.replace(/```json\s*/g, "") // Remove ```json
635-
.replace(/```\s*/g, "") // Remove any remaining ```
636-
.replace(/(\r\n|\n|\r)/g, "") // Remove line break
637-
.trim();
642+
const cleanedResponse = cleanJsonResponse(response);
638643

639644
// Try to parse as JSON first
640645
try {
641646
const jsonResponse = parseJSONObjectFromText(cleanedResponse);
642647
if (jsonResponse.text) {
643-
return this.trimTweetLength(jsonResponse.text);
648+
const truncateContent = truncateToCompleteSentence(
649+
jsonResponse.text,
650+
this.client.twitterConfig.MAX_TWEET_LENGTH,
651+
);
652+
return truncateContent;
644653
}
645654
if (typeof jsonResponse === "object") {
646655
const possibleContent =
647656
jsonResponse.content ||
648657
jsonResponse.message ||
649658
jsonResponse.response;
650659
if (possibleContent) {
651-
return this.trimTweetLength(possibleContent);
660+
const truncateContent = truncateToCompleteSentence(
661+
possibleContent,
662+
this.client.twitterConfig.MAX_TWEET_LENGTH,
663+
);
664+
return truncateContent;
652665
}
653666
}
654667
} catch (error) {
655668
error.linted = true; // make linter happy since catch needs a variable
656669

657670
// If JSON parsing fails, treat as plain text
658-
elizaLogger.debug("Response is not JSON, treating as plain text");
671+
elizaLogger.error(
672+
"Response is not JSON, treating as plain text",
673+
response,
674+
);
659675
}
660-
// If not JSON or no valid content found, clean the raw text
661-
return this.trimTweetLength(cleanedResponse);
662-
}
663676

664-
// Helper method to ensure tweet length compliance
665-
private trimTweetLength(text: string, maxLength = 280): string {
666-
if (text.length <= maxLength) return text;
677+
let truncateContent = truncateToCompleteSentence(
678+
extractAttributes(cleanedResponse, ["text"]).text,
679+
this.client.twitterConfig.MAX_TWEET_LENGTH,
680+
);
667681

668-
// Try to cut at last sentence
669-
const lastSentence = text.slice(0, maxLength).lastIndexOf(".");
670-
if (lastSentence > 0) {
671-
return text.slice(0, lastSentence + 1).trim();
682+
if (!truncateContent) {
683+
// If not JSON or no valid content found, clean the raw text
684+
truncateContent = truncateToCompleteSentence(
685+
cleanedResponse,
686+
this.client.twitterConfig.MAX_TWEET_LENGTH,
687+
);
672688
}
673689

674-
// Fallback to word boundary
675-
return (
676-
text.slice(0, text.lastIndexOf(" ", maxLength - 3)).trim() + "..."
677-
);
690+
return truncateContent;
678691
}
679692

680693
/**

0 commit comments

Comments
 (0)