Adam

 view release on metacpan or  search on metacpan

ex/ai-bot.pl  view on Meta::CPAN


  my $answer = eval {
    my $result = $self->_raider->raid($input);
    "$result";
  };

  if ($@ && $@ =~ /429|rate.limit/i) {
    my $total_wait = $self->_rate_limit_wait;
    my $err_channel = $self->_default_channel;
    if ($total_wait == 0) {
      # First hit — show brainfreeze (only in main channel)
      my $msg = $BRAINFREEZE[rand @BRAINFREEZE];
      $self->_send_to_channel($err_channel, $msg);
    }
    my $wait = $total_wait < 70 ? (70 - $total_wait) : 60;
    $self->_rate_limit_wait($total_wait + $wait);
    $self->info("Rate limited, total wait: " . $self->_rate_limit_wait . "s, next retry in ${wait}s");
    # Show another message every ~3 minutes of waiting
    if ($total_wait > 0 && int($total_wait / 180) != int($self->_rate_limit_wait / 180)) {
      my $msg = $BRAINFREEZE[rand @BRAINFREEZE];
      $self->_send_to_channel($err_channel, $msg);
    }
    POE::Kernel->delay( _retry_raid => $wait );
    return;
  }

  # Reset rate limit state
  $self->_rate_limit_wait(0);
  $self->_pending_raid(undef);

  if ($@) {
    $self->error("Raider error: $@");
    # Show error only in main channel
    $self->_send_to_channel($self->_default_channel,
      "Something broke in my brain. Getty probably forgot to feed the hamster that powers my GPU.");
    $self->_processing(0);
    $self->_schedule_pending_buffers;
    return;
  }

  # Log rate limit info
  eval {
    my $engine = $self->_raider->active_engine;
    if ($engine->has_rate_limit) {
      my $rl = $engine->rate_limit;
      $self->info(sprintf "Rate limit: %s requests remaining, %s tokens remaining",
        $rl->requests_remaining // '?', $rl->tokens_remaining // '?');
    }
  };

  $self->_processing(0);

  # Check for silence
  if ($answer =~ /__SILENT__/) {
    $self->info("Bert chose to stay silent");
    $self->_schedule_pending_buffers;
    return;
  }

  # Clean up AI output
  $answer =~ s/^<\s*\@?\s*(\w+)\s*>:?\s*/$1: /mg;     # line start <@nick> → Nick:
  $answer =~ s/<\s*\@?\s*(\w+)\s*>/$1/g;               # mid-text <nick> → Nick
  $answer =~ s/<\/?\w+>//g;                            # strip remaining XML tags
  # Strip lines where the AI narrates its tool usage
  $answer =~ s/^\*?\s*(save_note|recall_notes|update_note|delete_note|recall_history|stay_silent|set_alarm|whois|send_private_message)\b[^\n]*\n?//mg;

  # Check for lines too long
  my @lines = grep { length } map { s/^\s+//r =~ s/\s+$//r } split(/\n/, $answer);
  my $too_long = grep { length($_) > $MAX_LINE } @lines;
  if ($too_long) {
    $self->info("Response too long, asking to shorten");
    $answer = eval {
      my $retry = $self->_raider->raid(
        "Your last response had lines over $MAX_LINE characters. "
        . "Rewrite it shorter. Every line must be under $MAX_LINE chars."
      );
      "$retry";
    } || $answer;
  }

  # Store conversations
  for my $m (@$messages) {
    $self->memory->store_conversation(
      nick => $m->{nick}, message => $m->{msg},
      response => $answer, channel => $m->{channel},
    );
  }

  $self->_send_to_channel($channel, $answer);

  # Process any messages that arrived while we were thinking
  $self->_schedule_pending_buffers;
}

event _retry_raid => sub {
  my ($self) = $_[OBJECT];
  $self->info("Retrying raid...");
  $self->_do_raid;
};

event _alarm_fired => sub {
  my ( $self, $channel, $reason ) = @_[ OBJECT, ARG0, ARG1 ];
  $self->info("Alarm fired: $reason");
  $self->_buffer_message($channel, 'system',
    "ALARM FIRED: $reason — You set this alarm earlier. Decide what to do now.");
};

event _idle_check => sub {
  my ($self) = $_[OBJECT];
  my $idle_secs = time() - $self->_last_activity;
  if ($idle_secs >= $IDLE_PING && !$self->_processing) {
    my $idle_mins = int($idle_secs / 60);
    $self->info("Idle ping after ${idle_mins}m");
    # Ping first channel only (idle is a global concept)
    my $channel = $self->_default_channel;
    $self->_buffer_message($channel, 'system',
      "No activity for $idle_mins minutes. You can say something if you want, or stay_silent.");
  }
  POE::Kernel->delay( _idle_check => $IDLE_PING );
};



( run in 1.837 second using v1.01-cache-2.11-cpan-5837b0d9d2c )