Budget

NAME

MCP::Server::Tool::Web::Budget - the deadline and byte ledger one tool call spends

SYNOPSIS


use MCP::Server::Tool::Web::Budget;

# One budget per tool call. The clock starts now, not at the first request.
my $budget = MCP::Server::Tool::Web::Budget.new(
    total-seconds => 30,
    total-bytes   => 2 * 1024 * 1024,
    max-redirects => 5,
);

say $budget.remaining-seconds;      # 29.99...
say $budget.expired;                # False

# The ledger: ask for what a chunk needs, get what is left to give.
say $budget.take(1000);             # 1000 — all of it
say $budget.spent-bytes;            # 1000
say $budget.remaining-bytes;        # 2096152

# A body bigger than the budget is cut, and the cut is remembered.
my $big = MCP::Server::Tool::Web::Budget.new(total-bytes => 10);
say $big.take(4);                   # 4
say $big.take(99);                  # 6  — only six were left
say $big.truncated;                 # True
say $big.take(1);                   # 0  — nothing left at all

A budget is shared, not copied: one crawl hands the same object to every page it fetches, so twenty pages cannot each spend the whole allowance.


my $shared = MCP::Server::Tool::Web::Budget.new(total-bytes => 100_000);
for @urls -> $url {
    last if $shared.expired || !$shared.remaining-bytes;
    my $page = $fetcher.fetch($url, budget => $shared);
    say "{$url}: {$page.byte-count} bytes, {$shared.remaining-bytes} left";
}

DESCRIPTION

Three limits and a clock, in one object that everything on a single tool call shares. It is deliberately dumb: it has no idea what a socket is, holds no timer, starts no thread, and cannot fail. The Fetcher asks it questions and acts on the answers.

The clock starts at construction

total-seconds is measured from the moment the budget is made, not from the first request — connecting is part of what the caller is waiting for, and a crawl that spends its whole allowance on DNS should say so rather than run long. remaining-seconds never goes negative; it bottoms out at zero, which is what expired reports.

The ledger cuts, it does not throw

take($n) answers with how many of the $n bytes the budget could afford — $n when there is room, less when there is not, zero when there is none. That is the whole protocol: the caller appends what it was given, and if it was given less than it asked for it knows the body was cut and can stop reading. Nothing here throws, because a body that ran over a limit is a result ("here is the first 2 MB of it"), not an error.

truncated latches True the first time take gives back less than it was asked for. It is not set merely by the budget running out on a body that had already ended: the last chunk of a body that fits exactly is taken whole, and a take(0) asks for nothing and is given nothing.

Thread safety

The byte ledger is guarded by a lock. Body chunks arrive on the scheduler's threads rather than the caller's, and a crawl may one day fetch in parallel; an under-counted ledger would be a silent hole in the byte cap rather than a visible bug.

MCP::Server::Tool::Web v0.1.1

web search, fetch, crawl and grep for MCP::Server

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

MCP::Server:auth<zef:apogee>:ver<0.6.0+>Cro::HTTP:auth<zef:cro>:ver<0.8.11+>Cro::Core:auth<zef:cro>:ver<0.8.10+>IO::Socket::Async::SSL:auth<zef:raku-community-modules>:ver<0.8.2+>JSON::Fast:ver<0.19>:auth<cpan:TIMOTIMO>

Test Dependencies

Provides

  • MCP::Server::Tool::Web
  • MCP::Server::Tool::Web::Addr
  • MCP::Server::Tool::Web::Budget
  • MCP::Server::Tool::Web::Crawl
  • MCP::Server::Tool::Web::Extract
  • MCP::Server::Tool::Web::Fetcher
  • MCP::Server::Tool::Web::Guard
  • MCP::Server::Tool::Web::Provider::Brave
  • MCP::Server::Tool::Web::Robots
  • MCP::Server::Tool::Web::SearchProvider
  • MCP::Server::Tool::Web::Transport
  • MCP::Server::Tool::Web::Url
  • MCP::Server::Tool::Web::X

The Camelia image is copyright 2009 by Larry Wall. "Raku" is a trademark of the Yet Another Society. All rights reserved.

Built with Podlite — the markup and publishing tools behind this site.