Add ai usage to billing (#10138)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina
2025-10-24 12:02:39 +04:00
committed by GitHub
parent 6867dc9211
commit f2e0ca1453
45 changed files with 1001 additions and 65 deletions
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Počet souborů",
"OfficeSessionsDuration": "Doba schůzek",
"OfficeSessionsBandwidth": "Šířka pásma schůzek",
"OfficeEgressDuration": "Doba záznamu"
"OfficeEgressDuration": "Doba záznamu",
"AI": "AI",
"TranscriptionTime": "Doba přepisu",
"TotalTokens": "Celkem tokenů"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Dateianzahl",
"OfficeSessionsDuration": "Besprechungsdauer",
"OfficeSessionsBandwidth": "Besprechungsbandbreite",
"OfficeEgressDuration": "Aufzeichnungsdauer"
"OfficeEgressDuration": "Aufzeichnungsdauer",
"AI": "AI",
"TranscriptionTime": "Transkriptionszeit",
"TotalTokens": "Gesamte Tokens"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Files count",
"OfficeSessionsDuration": "Meetings time",
"OfficeSessionsBandwidth": "Meetings bandwidth",
"OfficeEgressDuration": "Recording time"
"OfficeEgressDuration": "Recording time",
"AI": "AI",
"TranscriptionTime": "Transcription time",
"TotalTokens": "Total tokens"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Cantidad de archivos",
"OfficeSessionsDuration": "Tiempo de reuniones",
"OfficeSessionsBandwidth": "Ancho de banda de reuniones",
"OfficeEgressDuration": "Tiempo de grabación"
"OfficeEgressDuration": "Tiempo de grabación",
"AI": "IA",
"TranscriptionTime": "Tiempo de transcripción",
"TotalTokens": "Total de tokens"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Nombre de fichiers",
"OfficeSessionsDuration": "Durée des réunions",
"OfficeSessionsBandwidth": "Bande passante des réunions",
"OfficeEgressDuration": "Durée d'enregistrement"
"OfficeEgressDuration": "Durée d'enregistrement",
"AI": "IA",
"TranscriptionTime": "Durée de transcription",
"TotalTokens": "Nombre total de jetons"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Conteggio file",
"OfficeSessionsDuration": "Durata riunioni",
"OfficeSessionsBandwidth": "Larghezza di banda riunioni",
"OfficeEgressDuration": "Durata registrazione"
"OfficeEgressDuration": "Durata registrazione",
"AI": "AI",
"TranscriptionTime": "Tempo di trascrizione",
"TotalTokens": "Totale token"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "ファイル数",
"OfficeSessionsDuration": "会議時間",
"OfficeSessionsBandwidth": "会議帯域幅",
"OfficeEgressDuration": "録画時間"
"OfficeEgressDuration": "録画時間",
"AI": "AI",
"TranscriptionTime": "文字起こし時間",
"TotalTokens": "合計トークン"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Quantidade de arquivos",
"OfficeSessionsDuration": "Tempo de reuniões",
"OfficeSessionsBandwidth": "Largura de banda de reuniões",
"OfficeEgressDuration": "Tempo de gravação"
"OfficeEgressDuration": "Tempo de gravação",
"AI": "IA",
"TranscriptionTime": "Tempo de transcrição",
"TotalTokens": "Total de tokens"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Количество файлов",
"OfficeSessionsDuration": "Время встреч",
"OfficeSessionsBandwidth": "Трафик встреч",
"OfficeEgressDuration": "Время записи"
"OfficeEgressDuration": "Время записи",
"AI": "AI",
"TranscriptionTime": "Время транскрипции",
"TotalTokens": "Всего токенов"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "Dosya sayısı",
"OfficeSessionsDuration": "Toplantı süresi",
"OfficeSessionsBandwidth": "Toplantı bant genişliği",
"OfficeEgressDuration": "Kayıt süresi"
"OfficeEgressDuration": "Kayıt süresi",
"AI": "AI",
"TranscriptionTime": "Transkripsiyon süresi",
"TotalTokens": "Toplam belirteçler"
}
}
+4 -1
View File
@@ -5,6 +5,9 @@
"DriveCount": "文件数量",
"OfficeSessionsDuration": "会议时间",
"OfficeSessionsBandwidth": "会议带宽",
"OfficeEgressDuration": "录制时间"
"OfficeEgressDuration": "录制时间",
"AI": "AI",
"TranscriptionTime": "转录时间",
"TotalTokens": "总令牌数"
}
}
@@ -13,15 +13,25 @@
// limitations under the License.
-->
<script lang="ts">
import { getBillingClient } from '../utils'
import { Breadcrumb, Header, Loading, Scroller, formatDuration, themeStore } from '@hcengineering/ui'
import {
Breadcrumb,
Header,
Loading,
Scroller,
formatDuration,
themeStore,
formatNumberCompact
} from '@hcengineering/ui'
import { getCurrentWorkspaceUuid } from '@hcengineering/presentation'
import billingPlugin from '@hcengineering/billing'
import filesize from 'filesize'
import StatsCard from './StatsCard.svelte'
import drivePlugin from '@hcengineering/drive'
import Category from './Category.svelte'
import love from '@hcengineering/love'
import drivePlugin from '@hcengineering/drive'
import view from '@hcengineering/view'
import { getBillingClient } from '../utils'
import StatsCard from './StatsCard.svelte'
import Category from './Category.svelte'
import ChartCard from './ChartCard.svelte'
const billingClient = getBillingClient()
@@ -34,6 +44,8 @@
let sessionsDurationByDay: { date: number, value: number }[] = []
let sessionsBandwidthByDay: { date: number, value: number }[] = []
let egressDurationByDay: { date: number, value: number }[] = []
let totalTranscriptDuration = 0
let totalTokensCount = 0
async function loadBillingData (): Promise<void> {
if (billingClient == null) return
@@ -43,6 +55,8 @@
totalSessionsDuration = billingStats.liveKitStats.sessions.reduce((sum, s) => sum + s.minutes, 0) * 60000
totalSessionsBandwidth = billingStats.liveKitStats.sessions.reduce((sum, s) => sum + s.bandwidth, 0)
totalEgressDuration = billingStats.liveKitStats.egress.reduce((sum, e) => sum + e.minutes, 0) * 60000
totalTranscriptDuration = billingStats.aiStats.transcript.totalDurationSeconds * 1000
totalTokensCount = billingStats.aiStats.tokens.reduce((sum, s) => sum + s.totalTokens, 0)
sessionsDurationByDay = billingStats.liveKitStats.sessions.map((s) => {
const date = new Date(Date.parse(s.day))
@@ -80,6 +94,15 @@
<StatsCard label={billingPlugin.string.DriveCount} text={totalDatalakeCount.toString()} />
</div>
</Category>
<Category icon={view.icon.AiStar} label={billingPlugin.string.AI}>
<div class="row">
<StatsCard
label={billingPlugin.string.TranscriptionTime}
text={formatDuration(totalTranscriptDuration, $themeStore.language)}
/>
<StatsCard label={billingPlugin.string.TotalTokens} text={formatNumberCompact(totalTokensCount)} />
</div>
</Category>
<Category icon={love.icon.Love} label={love.string.Office}>
<div class="row">
<StatsCard
+4 -1
View File
@@ -31,7 +31,10 @@ export const billingPlugin = plugin(billingId, {
DriveCount: '' as IntlString,
OfficeSessionsDuration: '' as IntlString,
OfficeSessionsBandwidth: '' as IntlString,
OfficeEgressDuration: '' as IntlString
OfficeEgressDuration: '' as IntlString,
AI: '' as IntlString,
TotalTokens: '' as IntlString,
TranscriptionTime: '' as IntlString
},
icon: {
Billing: '' as Asset
+10 -1
View File
@@ -278,4 +278,13 @@
<symbol id="print" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 4C7.89543 4 7 4.89543 7 6V26C7 27.1046 7.89543 28 9 28H23C24.1046 28 25 27.1046 25 26V12H21C18.7909 12 17 10.2091 17 8V4H9ZM19 4.41421V8C19 9.10457 19.8954 10 21 10H24.5858L19 4.41421ZM5 6C5 3.79086 6.79086 2 9 2H18.5858C19.1162 2 19.6249 2.21071 20 2.58579L26.4142 9C26.7893 9.37507 27 9.88378 27 10.4142V26C27 28.2091 25.2091 30 23 30H9C6.79086 30 5 28.2091 5 26V6ZM10 17C10 16.4477 10.4477 16 11 16H21C21.5523 16 22 16.4477 22 17C22 17.5523 21.5523 18 21 18H11C10.4477 18 10 17.5523 10 17ZM10 23C10 22.4477 10.4477 22 11 22H21C21.5523 22 22 22.4477 22 23C22 23.5523 21.5523 24 21 24H11C10.4477 24 10 23.5523 10 23Z" />
</symbol>
</svg>
<symbol id="ai-star" viewBox="0 0 24 24">
<path d="M19 22C19 22.5523 18.5523 23 18 23C17.4477 23 17 22.5523 17 22C17 21.4477 17.4477 21 18 21C18.5523 21 19 21.4477 19 22Z" fill="currentColor"/>
<path d="M23 18C23 18.5523 22.5523 19 22 19C21.4477 19 21 18.5523 21 18C21 17.4477 21.4477 17 22 17C22.5523 17 23 17.4477 23 18Z" fill="currentColor"/>
<path d="M3 6C3 6.55228 2.55228 7 2 7C1.44772 7 1 6.55228 1 6C1 5.44772 1.44772 5 2 5C2.55228 5 3 5.44772 3 6Z" fill="currentColor"/>
<path d="M7 2C7 2.55228 6.55228 3 6 3C5.44772 3 5 2.55228 5 2C5 1.44772 5.44772 1 6 1C6.55228 1 7 1.44772 7 2Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 6.48292L11.3478 9.17257C11.0871 10.2477 10.2477 11.0871 9.17257 11.3478L6.48292 12L9.17257 12.6522C10.2477 12.9129 11.0871 13.7523 11.3478 14.8274L12 17.5171L12.6522 14.8274C12.9129 13.7523 13.7523 12.9129 14.8274 12.6522L17.5171 12L14.8274 11.3478C13.7523 11.0871 12.9129 10.2477 12.6522 9.17257L12 6.48292ZM13.3052 5.02656C12.9733 3.65781 11.0267 3.65781 10.6948 5.02656L9.78151 8.79277C9.66301 9.28146 9.28146 9.66301 8.79277 9.78151L5.02656 10.6948C3.65781 11.0267 3.65781 12.9733 5.02656 13.3052L8.79277 14.2185C9.28146 14.337 9.66301 14.7185 9.78151 15.2072L10.6948 18.9734C11.0267 20.3422 12.9733 20.3422 13.3052 18.9734L14.2185 15.2072C14.337 14.7185 14.7185 14.337 15.2072 14.2185L18.9734 13.3052C20.3422 12.9733 20.3422 11.0267 18.9734 10.6948L15.2072 9.78151C14.7185 9.66301 14.337 9.28146 14.2185 8.79277L13.3052 5.02656Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00462 22.3147C8.83081 22.7804 8.31241 23.017 7.84673 22.8432C4.8303 21.7173 2.40732 19.3801 1.16973 16.4228C0.977847 15.9643 1.194 15.4371 1.65252 15.2452C2.11105 15.0533 2.63831 15.2694 2.8302 15.728C3.8762 18.2274 5.92654 20.2052 8.47615 21.1568C8.94183 21.3306 9.17843 21.849 9.00462 22.3147Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.3153 9.02077C21.8498 9.19493 21.3312 8.95873 21.157 8.49319C20.1979 5.92954 18.1971 3.87072 15.6693 2.83251C15.2095 2.64366 14.9899 2.11784 15.1787 1.65806C15.3676 1.19827 15.8934 0.978632 16.3532 1.16748C19.3427 2.39533 21.7076 4.82798 22.8429 7.86246C23.0171 8.32801 22.7809 8.8466 22.3153 9.02077Z" fill="currentColor"/>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

+2 -1
View File
@@ -68,7 +68,8 @@ loadMetadata(view.icon, {
MasterDetail: `${icons}#master-detail`,
Tree: `${icons}#tree`,
Document: `${icons}#document`,
Print: `${icons}#print`
Print: `${icons}#print`,
AiStar: `${icons}#ai-star`
})
loadMetadata(core.icon, {
TypeString: `${icons}#string`,
+2 -1
View File
@@ -304,7 +304,8 @@ const view = plugin(viewId, {
MasterDetail: '' as Asset,
Tree: '' as Asset,
Document: '' as Asset,
Print: '' as Asset
Print: '' as Asset,
AiStar: '' as Asset
},
category: {
General: '' as Ref<ActionCategory>,