@photon-ai/flux 0.6.3 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -249,6 +249,37 @@ The `splitIntoMessages` helper function splits messages using `\n` and then loop
249
249
 
250
250
  For example, `"Hello!\nHow are you?\nNice to meet you!"` will be sent as three separate message bubbles.
251
251
 
252
+ ## Tapbacks
253
+
254
+ Agents can send tapback reactions (love, like, dislike, laugh, emphasize, question). When your agent receives a message, it can react to it using the `sendTapback` function.
255
+
256
+ To use `sendTapback`, you need to:
257
+ 1. Capture `sendTapback` in `onInit` once at startup
258
+ 2. Call it in `invoke` when processing messages
259
+
260
+ ```
261
+ import { FluxAgent, SendTapbackFn } from '@photon-ai/flux';
262
+
263
+ let sendTapback: SendTapbackFn | undefined;
264
+
265
+ const agent: FluxAgent = {
266
+ onInit: async (_sendMessage, _sendTapback) => {
267
+ sendTapback = _sendTapback; // Save it for later
268
+ },
269
+
270
+ invoke: async ({ message, userPhoneNumber, messageGuid }) => {
271
+ // Now you can use it
272
+ if (sendTapback && messageGuid) {
273
+ await sendTapback(messageGuid, 'love', userPhoneNumber);
274
+ }
275
+
276
+ return "Hello!";
277
+ },
278
+ };
279
+
280
+ export default agent;
281
+ ```
282
+
252
283
  ## 💡 Examples
253
284
 
254
285
  ### Weather Agent
@@ -313,6 +344,56 @@ export default {
313
344
  };
314
345
  ```
315
346
 
347
+ ### Chatbot with Tapbacks
348
+
349
+ A conversational chatbot with tapback functionalities:
350
+
351
+ ```
352
+ import "dotenv/config";
353
+ import OpenAI from "openai";
354
+
355
+ const openai = new OpenAI();
356
+ const conversations = new Map<string, Array<{ role: "user" | "assistant" | "system"; content: string }>>();
357
+
358
+ let sendTapback: ((messageGuid: string, reaction: string, userPhoneNumber: string) => Promise<boolean>) | undefined;
359
+
360
+ export default {
361
+ onInit: async (_sendMessage: any, _sendTapback: any) => {
362
+ sendTapback = _sendTapback;
363
+ },
364
+
365
+ invoke: async ({ message, userPhoneNumber, messageGuid }: { message: string; userPhoneNumber: string; messageGuid?: string }) => {
366
+ // Get or create conversation history
367
+ if (!conversations.has(userPhoneNumber)) {
368
+ conversations.set(userPhoneNumber, [{
369
+ role: "system",
370
+ content: "You are a friendly iMessage assistant. Keep responses concise. For positive messages, start with [TAPBACK:love], [TAPBACK:laugh], or [TAPBACK:like]."
371
+ }]);
372
+ }
373
+ const history = conversations.get(userPhoneNumber)!;
374
+ history.push({ role: "user", content: message });
375
+
376
+ // Call OpenAI
377
+ const completion = await openai.chat.completions.create({
378
+ model: "gpt-4o-mini",
379
+ messages: history,
380
+ });
381
+
382
+ let response = completion.choices[0]?.message?.content || "Hello!";
383
+
384
+ // Extract and send tapback if present
385
+ const tapbackMatch = response.match(/^\[TAPBACK:(love|like|laugh|emphasize)\]/i);
386
+ if (tapbackMatch && sendTapback && messageGuid) {
387
+ await sendTapback(messageGuid, tapbackMatch[1].toLowerCase(), userPhoneNumber);
388
+ response = response.replace(tapbackMatch[0], "").trim();
389
+ }
390
+
391
+ history.push({ role: "assistant", content: response });
392
+ return response;
393
+ },
394
+ };
395
+ ```
396
+
316
397
  ## Why Flux?
317
398
 
318
399
  Connecting agents to messaging platforms traditionally involves complex processes like setting up servers, configuring webhooks, and dealing with platform APIs. Most solutions rely on SMS or WhatsApp, which can be unintuitive for many users.
package/dist/index.js CHANGED
@@ -52185,26 +52185,20 @@ var FluxClient = class {
52185
52185
  }
52186
52186
  return result.success;
52187
52187
  }
52188
- async sendTapback(messageGuid, reaction) {
52188
+ async sendTapback(messageGuid, reaction, chat) {
52189
52189
  if (!this.client) throw new Error("Not connected. Call connect() first.");
52190
- const payload = { messageGuid, reaction };
52191
- console.log(`[FLUX DEBUG] sendTapback called`);
52192
- console.log(`[FLUX DEBUG] Payload: ${JSON.stringify(payload)}`);
52193
- console.log(`[FLUX DEBUG] Payload keys: ${Object.keys(payload).join(", ")}`);
52194
- console.log(`[FLUX DEBUG] Server: ${GRPC_SERVER_ADDRESS}`);
52195
52190
  try {
52196
- console.log(`[FLUX DEBUG] Calling client.FluxService.sendTapback...`);
52197
- const result = await this.client.FluxService.sendTapback(payload);
52198
- console.log(`[FLUX DEBUG] Result: ${JSON.stringify(result)}`);
52199
- if (result.success) {
52200
- console.log(`[FLUX] Tapback sent successfully!`);
52201
- } else {
52191
+ const result = await this.client.FluxService.sendTapback({
52192
+ messageGuid,
52193
+ reaction,
52194
+ chat
52195
+ });
52196
+ if (!result.success) {
52202
52197
  console.error(`[FLUX] Tapback failed: ${result.error}`);
52203
52198
  }
52204
52199
  return result.success;
52205
52200
  } catch (error) {
52206
- console.error(`[FLUX DEBUG] Exception caught: ${error.message}`);
52207
- console.error(`[FLUX DEBUG] Error stack: ${error.stack}`);
52201
+ console.error(`[FLUX] Tapback error: ${error.message}`);
52208
52202
  return false;
52209
52203
  }
52210
52204
  }
@@ -52422,6 +52416,37 @@ async function loadAgent(agentPath) {
52422
52416
  return agent;
52423
52417
  }
52424
52418
 
52419
+ // src/memory.ts
52420
+ var Memory = class {
52421
+ store = /* @__PURE__ */ new Map();
52422
+ // Add a message to a conversation
52423
+ add(phoneNumber, message) {
52424
+ const normalized = this.normalizePhone(phoneNumber);
52425
+ if (!this.store.has(normalized)) {
52426
+ this.store.set(normalized, []);
52427
+ }
52428
+ this.store.get(normalized).push(message);
52429
+ }
52430
+ // Get all messages for a conversation
52431
+ get(phoneNumber) {
52432
+ const normalized = this.normalizePhone(phoneNumber);
52433
+ return this.store.get(normalized) || [];
52434
+ }
52435
+ // Clear history for a specific conversation, or all if no phone provided
52436
+ clear(phoneNumber) {
52437
+ if (phoneNumber) {
52438
+ this.store.delete(this.normalizePhone(phoneNumber));
52439
+ } else {
52440
+ this.store.clear();
52441
+ }
52442
+ }
52443
+ // Normalize phone number format
52444
+ normalizePhone(phone) {
52445
+ return phone.replace(/[\s\-\(\)]/g, "");
52446
+ }
52447
+ };
52448
+ var memory = new Memory();
52449
+
52425
52450
  // src/index.ts
52426
52451
  function splitIntoMessages2(response) {
52427
52452
  if (!response.includes("\n")) {
@@ -52469,17 +52494,29 @@ async function runLocal() {
52469
52494
  input: process.stdin,
52470
52495
  output: process.stdout
52471
52496
  });
52497
+ const localPhoneNumber = "+1234567890";
52472
52498
  const askQuestion = () => {
52473
52499
  rl.question("You: ", async (input) => {
52474
52500
  if (!input.trim()) {
52475
52501
  askQuestion();
52476
52502
  return;
52477
52503
  }
52504
+ memory.add(localPhoneNumber, {
52505
+ role: "user",
52506
+ content: input,
52507
+ timestamp: Date.now()
52508
+ });
52478
52509
  console.log("[FLUX] Thinking...");
52479
52510
  try {
52480
52511
  const response = await agent.invoke({
52481
52512
  message: input,
52482
- userPhoneNumber: "+1234567890"
52513
+ userPhoneNumber: localPhoneNumber,
52514
+ history: memory.get(localPhoneNumber)
52515
+ });
52516
+ memory.add(localPhoneNumber, {
52517
+ role: "assistant",
52518
+ content: response,
52519
+ timestamp: Date.now()
52483
52520
  });
52484
52521
  const messages = splitIntoMessages2(response);
52485
52522
  for (const msg of messages) {
@@ -52523,12 +52560,24 @@ async function runProd() {
52523
52560
  console.log("[FLUX] Agent loaded successfully!");
52524
52561
  const flux = new FluxClient(phoneNumber, token, async (message) => {
52525
52562
  console.log(`[FLUX] Processing message from ${message.userPhoneNumber}: ${message.text}`);
52563
+ memory.add(message.userPhoneNumber, {
52564
+ role: "user",
52565
+ content: message.text,
52566
+ timestamp: Date.now(),
52567
+ imageBase64: message.imageBase64
52568
+ });
52526
52569
  try {
52527
52570
  const response = await agent.invoke({
52528
52571
  message: message.text,
52529
52572
  userPhoneNumber: message.userPhoneNumber,
52530
52573
  messageGuid: message.messageGuid,
52531
- imageBase64: message.imageBase64
52574
+ imageBase64: message.imageBase64,
52575
+ history: memory.get(message.userPhoneNumber)
52576
+ });
52577
+ memory.add(message.userPhoneNumber, {
52578
+ role: "assistant",
52579
+ content: response,
52580
+ timestamp: Date.now()
52532
52581
  });
52533
52582
  console.log(`[FLUX] Agent response: ${response}`);
52534
52583
  return response;
@@ -52550,8 +52599,8 @@ async function runProd() {
52550
52599
  }
52551
52600
  return true;
52552
52601
  },
52553
- async (messageGuid, reaction) => {
52554
- return flux.sendTapback(messageGuid, reaction);
52602
+ async (messageGuid, reaction, chat) => {
52603
+ return flux.sendTapback(messageGuid, reaction, chat);
52555
52604
  }
52556
52605
  );
52557
52606
  }