@photon-ai/flux 0.6.5 → 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
@@ -52416,6 +52416,37 @@ async function loadAgent(agentPath) {
52416
52416
  return agent;
52417
52417
  }
52418
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
+
52419
52450
  // src/index.ts
52420
52451
  function splitIntoMessages2(response) {
52421
52452
  if (!response.includes("\n")) {
@@ -52463,17 +52494,29 @@ async function runLocal() {
52463
52494
  input: process.stdin,
52464
52495
  output: process.stdout
52465
52496
  });
52497
+ const localPhoneNumber = "+1234567890";
52466
52498
  const askQuestion = () => {
52467
52499
  rl.question("You: ", async (input) => {
52468
52500
  if (!input.trim()) {
52469
52501
  askQuestion();
52470
52502
  return;
52471
52503
  }
52504
+ memory.add(localPhoneNumber, {
52505
+ role: "user",
52506
+ content: input,
52507
+ timestamp: Date.now()
52508
+ });
52472
52509
  console.log("[FLUX] Thinking...");
52473
52510
  try {
52474
52511
  const response = await agent.invoke({
52475
52512
  message: input,
52476
- 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()
52477
52520
  });
52478
52521
  const messages = splitIntoMessages2(response);
52479
52522
  for (const msg of messages) {
@@ -52517,12 +52560,24 @@ async function runProd() {
52517
52560
  console.log("[FLUX] Agent loaded successfully!");
52518
52561
  const flux = new FluxClient(phoneNumber, token, async (message) => {
52519
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
+ });
52520
52569
  try {
52521
52570
  const response = await agent.invoke({
52522
52571
  message: message.text,
52523
52572
  userPhoneNumber: message.userPhoneNumber,
52524
52573
  messageGuid: message.messageGuid,
52525
- 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()
52526
52581
  });
52527
52582
  console.log(`[FLUX] Agent response: ${response}`);
52528
52583
  return response;