| 592 | } |
| 593 | |
| 594 | function makeUsageService(sdk: OpencodeClient) { |
| 595 | const limits = new Map<string, Promise<number | undefined>>() |
| 596 | const contextLimit: UsageService.Interface["contextLimit"] = Effect.fn("ACP.promptUsage.contextLimit")( |
| 597 | function* (params) { |
| 598 | const key = `${params.directory}\u0000${params.providerID}\u0000${params.modelID}` |
| 599 | const current = limits.get(key) |
| 600 | if (current) return yield* Effect.promise(() => current) |
| 601 | |
| 602 | const next = sdk.config |
| 603 | .providers({ directory: params.directory }, { throwOnError: true }) |
| 604 | .then((response) => { |
| 605 | const providers = Object.fromEntries( |
| 606 | (response.data?.providers ?? []).map((provider) => [provider.id, provider]), |
| 607 | ) as Record<ProviderV2.ID, Provider.Info> |
| 608 | return UsageService.findContextLimit(providers, params.providerID, params.modelID) |
| 609 | }) |
| 610 | .catch(() => undefined) |
| 611 | limits.set(key, next) |
| 612 | return yield* Effect.promise(() => next) |
| 613 | }, |
| 614 | ) |
| 615 | |
| 616 | const sendUpdate: UsageService.Interface["sendUpdate"] = Effect.fn("ACP.promptUsage.sendUpdate")(function* (params) { |
| 617 | const messages = yield* request( |
| 618 | () => |
| 619 | sdk.session.messages( |
| 620 | { |
| 621 | sessionID: params.sessionID, |
| 622 | directory: params.directory, |
| 623 | }, |
| 624 | { throwOnError: true }, |
| 625 | ), |
| 626 | "session", |
| 627 | ).pipe( |
| 628 | Effect.map((messages) => messages as readonly UsageService.SessionMessage[]), |
| 629 | Effect.catch((error) => |
| 630 | Effect.logError("failed to fetch messages for usage update", { error: error }).pipe(Effect.as(undefined)), |
| 631 | ), |
| 632 | ) |
| 633 | if (!messages) return |
| 634 | |
| 635 | const message = UsageService.latestAssistantMessage(messages) |
| 636 | if (!message?.providerID || !message.modelID) return |
| 637 | |
| 638 | const size = yield* contextLimit({ |
| 639 | directory: params.directory, |
| 640 | providerID: ProviderV2.ID.make(message.providerID), |
| 641 | modelID: ModelV2.ID.make(message.modelID), |
| 642 | }) |
| 643 | if (!size) return |
| 644 | |
| 645 | yield* Effect.promise(() => |
| 646 | params.connection |
| 647 | .sessionUpdate({ |
| 648 | sessionId: params.sessionID, |
| 649 | update: { |
| 650 | sessionUpdate: "usage_update", |
| 651 | used: message.tokens.input + message.tokens.cache.read, |