Repository navigation
fix(provider-muse): Muse Code's subscription usage shows in Usage limits #18872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| import { ProviderDriverKind } from "@t3tools/contracts"; | ||
| import { MuseSettings } from "../settings.ts"; | ||
| import * as HostProcess from "@t3tools/shared/HostProcess"; | ||
| import * as DateTime from "effect/DateTime"; | ||
| import * as Effect from "effect/Effect"; | ||
| import * as FileSystem from "effect/FileSystem"; | ||
| import * as Path from "effect/Path"; | ||
| import * as Ref from "effect/Ref"; | ||
| import * as Schema from "effect/Schema"; | ||
| import * as Semaphore from "effect/Semaphore"; | ||
| import * as HttpClient from "effect/http/HttpClient"; | ||
| import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; | ||
|
|
||
|
|
@@ -22,6 +25,14 @@ import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEve | |
| import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider"; | ||
| import { enrichMuseSnapshot, latestMuseVersion, museMaintenance } from "./maintenance.ts"; | ||
| import { makeMuseEnvironment } from "./sdk.ts"; | ||
| import type { MuseSubscriptionUsage } from "./protocol.ts"; | ||
| import { | ||
| isNewerMuseUsageReport, | ||
| museSubscriptionUsageToLimits, | ||
| museSubscriptionUsageToUpdate, | ||
| } from "./usageLimits.ts"; | ||
| import { makeUnavailableUsageLimits } from "@t3tools/provider-core/server/usageLimits"; | ||
| import type { ServerProviderDraft } from "@t3tools/provider-core/server/snapshotProbe"; | ||
| import { | ||
| defaultProviderContinuationIdentity, | ||
| type ProviderDriver, | ||
|
|
@@ -92,6 +103,46 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = { | |
| continuationGroupKey: continuationIdentity.continuationKey, | ||
| }); | ||
| const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | ||
| // Meta reports subscription usage only with a reply, and a status check | ||
| // makes none. Keep the newest report from any of this instance's | ||
| // sessions so a refresh re-derives the windows instead of dropping | ||
| // them. Reports and status checks take turns through one permit, so | ||
| // comparing, retaining and publishing a report is a single step: an | ||
| // older report arriving late can neither overwrite nor publish over a | ||
| // newer one. | ||
| const retainedUsage = yield* Ref.make<MuseSubscriptionUsage | undefined>(undefined); | ||
| const usagePermit = yield* Semaphore.make(1); | ||
| // Only the instance's own environment counts: an inherited META_API_KEY | ||
| // was already dropped so Muse uses its login. | ||
| const usesApiKey = Object.entries(processEnvironment).some( | ||
| ([key, value]) => key.toUpperCase() === "META_API_KEY" && (value ?? "").trim() !== "", | ||
| ); | ||
| const withUsageLimits = (snapshot: ServerProviderDraft) => | ||
| usagePermit.withPermits(1)( | ||
| Effect.gen(function* () { | ||
| if (!effectiveConfig.enabled || !snapshot.installed) return snapshot; | ||
| const now = yield* DateTime.now; | ||
| if (usesApiKey) { | ||
| return { | ||
| ...snapshot, | ||
| usageLimits: makeUnavailableUsageLimits({ | ||
| checkedAt: DateTime.formatIso(now), | ||
| reason: "unsupported", | ||
| message: "Muse uses an API key here, which does not report subscription limits.", | ||
| }), | ||
| }; | ||
| } | ||
| const usage = yield* Ref.get(retainedUsage); | ||
| const usageLimits = usage | ||
| ? museSubscriptionUsageToLimits({ usage, nowMs: DateTime.toEpochMillis(now) }) | ||
| : makeUnavailableUsageLimits({ | ||
| checkedAt: DateTime.formatIso(now), | ||
| reason: "probeFailed", | ||
| message: "Muse reports usage limits with its next reply.", | ||
| }); | ||
| return { ...snapshot, usageLimits }; | ||
| }), | ||
| ); | ||
| const resolveInstallation = yield* makeCachedProviderMaintenanceResolution( | ||
| resolveProviderMaintenanceCapabilitiesEffect(museMaintenance, { | ||
| binaryPath: effectiveConfig.binaryPath, | ||
|
|
@@ -126,6 +177,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = { | |
| initialSnapshot: (settings) => | ||
| makePendingMuseProvider(settings.provider).pipe(Effect.map(stampIdentity)), | ||
| checkProvider: checkMuseProviderStatus(effectiveConfig, processEnvironment, cwd).pipe( | ||
| Effect.flatMap(withUsageLimits), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium A refresh can overwrite newer reply usage with stale probe limits, leaving the displayed limits stale until another refresh or newer report arrives. 🤖 Copy this AI Prompt to have your agent fix this: |
||
| Effect.map(stampIdentity), | ||
| Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | ||
| Effect.provideService(FileSystem.FileSystem, fileSystem), | ||
|
|
@@ -163,6 +215,24 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = { | |
| environment: processEnvironment, | ||
| modelCatalog, | ||
| ...(eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}), | ||
| onSubscriptionUsage: (usage) => | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a Muse instance is used for automatic thread/branch titles or commit/PR text, those calls go through Useful? React with 👍 / 👎. |
||
| usagePermit | ||
| .withPermits(1)( | ||
| Effect.gen(function* () { | ||
| // Every session's host reports; only the newest wins. | ||
| const newer = yield* Ref.modify(retainedUsage, (previous) => | ||
| isNewerMuseUsageReport(previous, usage) | ||
| ? ([true, usage] as const) | ||
| : ([false, previous] as const), | ||
| ); | ||
| if (!newer || usesApiKey) return; | ||
| const now = yield* DateTime.now; | ||
| yield* snapshot.applyUsageLimits( | ||
| museSubscriptionUsageToUpdate({ usage, nowMs: DateTime.toEpochMillis(now) }), | ||
| ); | ||
| }), | ||
| ) | ||
| .pipe(Effect.ignoreCause({ log: true })), | ||
| continuationRequests, | ||
| }); | ||
| const textGeneration = yield* makeMuseTextGeneration(effectiveConfig, { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: pingdotgg/t3code
Length of output: 5094
🏁 Script executed:
Repository: pingdotgg/t3code
Length of output: 45661
🏁 Script executed:
Repository: pingdotgg/t3code
Length of output: 42242
🏁 Script executed:
Repository: pingdotgg/t3code
Length of output: 17201
State the Muse pooling exception.
The general guidance says that the same account across environments counts once. Muse does not provide account attribution, so the same Meta account can appear separately for each Muse instance. Add this exception.
Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents