Add managed content and keyboard usage insights
Introduce versioned official content workflows and privacy-safe keyboard analytics, while preventing repeat DeviceCheck sign-ins from incorrectly restricting eligible accounts.
This commit is contained in:
@@ -7,6 +7,9 @@
|
||||
- Product analytics installations linked to the account and all of their events
|
||||
are deleted by database cascade. The service stores only the digest of a
|
||||
random installation UUID and never stores user content in analytics events.
|
||||
- Keyboard usage contains daily Chinese, English, other-character and
|
||||
input-session counters only. It never contains text or keystrokes and is
|
||||
purged after 90 days regardless of account linkage.
|
||||
- Pseudonymous immutable credit-ledger entries, StoreKit transaction audit data,
|
||||
and time-limited anti-abuse tombstones remain after deletion where required to
|
||||
prevent replay, preserve financial integrity, and stop repeated trial abuse.
|
||||
|
||||
@@ -69,6 +69,51 @@ Managed client success events are excluded from this total.
|
||||
|
||||
`successful AI requests / distinct value-active users` for the selected period.
|
||||
|
||||
## Keyboard input usage
|
||||
|
||||
Keyboard input metrics use finalized UTC-day summaries produced on-device.
|
||||
They describe manually committed OSGKeyboard text only and are independent
|
||||
from AI value events, billing and referral qualification.
|
||||
|
||||
### Keyboard input active users
|
||||
|
||||
Distinct account identities, falling back to pseudonymous installations, with
|
||||
at least one accepted keyboard usage summary in the selected UTC-date window.
|
||||
|
||||
### Activation-to-input conversion
|
||||
|
||||
Keyboard input active users divided by distinct identities with either a
|
||||
`KEYBOARD_ACTIVATED` event or an accepted keyboard usage summary in the same
|
||||
UTC-date window. Including summary-only identities prevents missing activation
|
||||
telemetry from producing rates above 100%.
|
||||
|
||||
### Chinese, English and bilingual active users
|
||||
|
||||
- Chinese active: at least one committed Han-script character.
|
||||
- English active: at least one committed Latin letter.
|
||||
- Bilingual active: both Chinese and English counts are non-zero.
|
||||
|
||||
These populations overlap and must not be summed.
|
||||
|
||||
### Character volume and language share
|
||||
|
||||
Character volume is the sum of client-classified Chinese, English and other
|
||||
committed characters. Chinese and English share use only classified language
|
||||
characters as the denominator:
|
||||
|
||||
- Chinese share: `Chinese / (Chinese + English)`.
|
||||
- English share: `English / (Chinese + English)`.
|
||||
|
||||
Both shares are unavailable when the denominator is zero. Other characters
|
||||
remain visible in total volume but do not dilute the language split.
|
||||
|
||||
### Input sessions
|
||||
|
||||
An input session is a keyboard activation containing at least one manually
|
||||
committed character. Chinese-only, English-only, mixed-language and other-only
|
||||
session counts form a complete partition. Average characters per input session
|
||||
is `total committed characters / input sessions`.
|
||||
|
||||
## Retention
|
||||
|
||||
The cohort date is the UTC date of a user's first successful AI feature.
|
||||
|
||||
@@ -8,7 +8,7 @@ from billing, provider execution and immutable credit ledgers.
|
||||
Never send or persist:
|
||||
|
||||
- audio or audio-derived content;
|
||||
- keyboard input, prompts, context, transcripts or model output;
|
||||
- raw keyboard input, keystrokes, prompts, context, transcripts or model output;
|
||||
- Apple subjects, email addresses, names, tokens, API keys or provider
|
||||
credentials;
|
||||
- arbitrary property names or free-form text.
|
||||
@@ -18,6 +18,8 @@ the identifier supplied by the client. Event DTO string representations are
|
||||
redacted. Product analytics rows linked to an account are deleted with that
|
||||
account. Anonymous installations that never link to an account are retained for
|
||||
90 days and are eligible for scheduled deletion in a later operational job.
|
||||
Per-installation keyboard usage summaries are retained for at most 90 days
|
||||
regardless of account linkage.
|
||||
|
||||
## Ingestion API
|
||||
|
||||
@@ -40,6 +42,38 @@ account. Anonymous installations that never link to an account are retained for
|
||||
The successful response reports `accepted` and `replayed` event counts. It does
|
||||
not return account or installation identifiers.
|
||||
|
||||
## Keyboard usage summaries
|
||||
|
||||
`POST /v1/analytics/keyboard-usage` accepts 1 to 50 finalized UTC-day
|
||||
summaries. Authentication, installation hashing, account linking, atomic
|
||||
batching and replay semantics match the event ingestion API.
|
||||
|
||||
- Language classification and counting happen on-device.
|
||||
- Only text manually committed by OSGKeyboard is counted. Voice transcripts,
|
||||
pasted text, AI-generated output and text entered with another keyboard are
|
||||
excluded.
|
||||
- Chinese counts Unicode characters classified with the Han script. English
|
||||
counts Latin letters. Digits, punctuation and Emoji are counted as `other`.
|
||||
- Pinyin composition keystrokes are not counted as English; only the final
|
||||
committed text is classified.
|
||||
- An input session is one keyboard activation containing at least one
|
||||
committed character.
|
||||
- Chinese-only sessions contain Chinese but no English; English-only sessions
|
||||
contain English but no Chinese; mixed sessions contain both. Other
|
||||
characters may occur in any of those sessions. Other-only sessions contain
|
||||
neither Chinese nor English.
|
||||
- The four language-session counters must sum to `inputSessionCount`, and the
|
||||
total committed character count must be at least the session count.
|
||||
- Each installation may submit only one immutable summary per UTC date.
|
||||
- Accepted dates are from 35 days ago through yesterday. Current-day partial
|
||||
summaries are rejected.
|
||||
- Raw text, per-keystroke events, surrounding context, host application
|
||||
identifiers and free-form properties are never accepted.
|
||||
|
||||
The client should keep counters and the finalized outbox in the shared App
|
||||
Group so either the containing app or keyboard extension can deliver them.
|
||||
Retries must reuse the stored `clientSummaryId`.
|
||||
|
||||
## Event catalog
|
||||
|
||||
### Lifecycle events
|
||||
@@ -186,6 +220,8 @@ The admin analytics endpoints combine:
|
||||
- settled credit usage for managed AI counts and credit consumption;
|
||||
- accounts, StoreKit and referrals for registration, monetization and referral
|
||||
outcomes.
|
||||
- daily keyboard summaries for privacy-minimized Chinese and English input
|
||||
activity, character volume and input sessions.
|
||||
|
||||
Metric formulas are defined in
|
||||
[`ANALYTICS_METRICS_DICTIONARY.md`](ANALYTICS_METRICS_DICTIONARY.md).
|
||||
|
||||
@@ -21,6 +21,7 @@ GRANT SELECT ON osg_account.credit_reservations TO 'osg_account_runtime'@'10.20.
|
||||
GRANT SELECT ON osg_account.referral_campaigns TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.referral_campaign_budgets TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.referral_codes TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.referral_owner_codes TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.referral_bindings TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.credit_usage_records TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.credit_ledger TO 'osg_account_runtime'@'10.20.%';
|
||||
@@ -44,6 +45,11 @@ GRANT SELECT ON osg_account.storekit_credit_purchases TO 'osg_account_runtime'@'
|
||||
GRANT SELECT ON osg_account.product_analytics_installations TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.product_analytics_events TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.product_analytics_daily_counters TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.keyboard_usage_daily_summaries TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.official_content_catalog TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.official_skills TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.official_skill_localizations TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT SELECT ON osg_account.official_hint_packs TO 'osg_account_runtime'@'10.20.%';
|
||||
|
||||
GRANT INSERT, UPDATE, DELETE ON osg_account.accounts TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT, UPDATE ON osg_account.apple_credentials TO 'osg_account_runtime'@'10.20.%';
|
||||
@@ -53,6 +59,7 @@ GRANT INSERT, UPDATE ON osg_account.credit_accounts TO 'osg_account_runtime'@'10
|
||||
GRANT INSERT, UPDATE ON osg_account.credit_reservations TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT UPDATE ON osg_account.referral_campaign_budgets TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT ON osg_account.referral_codes TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT ON osg_account.referral_owner_codes TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT, UPDATE ON osg_account.referral_bindings TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT ON osg_account.credit_usage_records TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT ON osg_account.credit_ledger TO 'osg_account_runtime'@'10.20.%';
|
||||
@@ -81,6 +88,12 @@ GRANT INSERT, UPDATE, DELETE ON osg_account.product_analytics_installations
|
||||
GRANT INSERT ON osg_account.product_analytics_events TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT, UPDATE ON osg_account.product_analytics_daily_counters
|
||||
TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT, DELETE ON osg_account.keyboard_usage_daily_summaries
|
||||
TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT UPDATE ON osg_account.official_content_catalog TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT, UPDATE ON osg_account.official_skills TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT, UPDATE ON osg_account.official_skill_localizations TO 'osg_account_runtime'@'10.20.%';
|
||||
GRANT INSERT, UPDATE ON osg_account.official_hint_packs TO 'osg_account_runtime'@'10.20.%';
|
||||
|
||||
-- Deliberately absent: global privileges, GRANT OPTION, FILE, PROCESS, SUPER,
|
||||
-- CREATE USER, and UPDATE/DELETE on immutable ledger or usage-history tables.
|
||||
|
||||
@@ -100,6 +100,33 @@ paths:
|
||||
"409": { $ref: "#/components/responses/Error" }
|
||||
"422": { $ref: "#/components/responses/Error" }
|
||||
default: { $ref: "#/components/responses/Error" }
|
||||
/v1/analytics/keyboard-usage:
|
||||
post:
|
||||
security:
|
||||
- {}
|
||||
- bearerAuth: []
|
||||
summary: Idempotently accept privacy-minimized daily keyboard usage summaries
|
||||
description: |
|
||||
Accepts finalized UTC-day counters for text manually committed by
|
||||
OSGKeyboard. Language classification happens on-device. Raw text,
|
||||
keystrokes, surrounding context, host application identifiers, voice
|
||||
transcripts and AI output are never accepted. The current UTC date is
|
||||
not accepted because daily summaries are immutable once submitted.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/KeyboardUsageBatchRequest" }
|
||||
responses:
|
||||
"200":
|
||||
description: Atomic batch acceptance and replay counts
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/ProductAnalyticsBatchResponse" }
|
||||
"400": { $ref: "#/components/responses/Error" }
|
||||
"409": { $ref: "#/components/responses/Error" }
|
||||
"422": { $ref: "#/components/responses/Error" }
|
||||
default: { $ref: "#/components/responses/Error" }
|
||||
/v1/account:
|
||||
get:
|
||||
summary: Return the account profile
|
||||
@@ -477,6 +504,100 @@ paths:
|
||||
responses:
|
||||
"101": { description: WebSocket upgrade }
|
||||
default: { $ref: "#/components/responses/GatewayError" }
|
||||
/v1/content/skills:
|
||||
get:
|
||||
security: []
|
||||
summary: Return the enabled official Skill catalog
|
||||
parameters:
|
||||
- name: If-None-Match
|
||||
in: header
|
||||
schema: { type: string }
|
||||
responses:
|
||||
"200":
|
||||
description: Versioned official Skill catalog
|
||||
headers:
|
||||
ETag: { schema: { type: string } }
|
||||
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/OfficialSkillCatalog" }
|
||||
"304": { description: The caller already has the current revision }
|
||||
/v1/content/hints/manifest:
|
||||
get:
|
||||
security: []
|
||||
summary: Return the published AI Hint pack manifest
|
||||
parameters:
|
||||
- name: If-None-Match
|
||||
in: header
|
||||
schema: { type: string }
|
||||
responses:
|
||||
"200":
|
||||
description: Published locale manifest
|
||||
headers:
|
||||
ETag: { schema: { type: string } }
|
||||
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AIHintManifest" }
|
||||
"304": { description: The caller already has the current manifest }
|
||||
/v1/content/hints/{locale}:
|
||||
get:
|
||||
security: []
|
||||
summary: Return a published AI Hint pack
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/HintLocale"
|
||||
- name: If-None-Match
|
||||
in: header
|
||||
schema: { type: string }
|
||||
responses:
|
||||
"200":
|
||||
description: Published AI Hint pack
|
||||
headers:
|
||||
ETag: { schema: { type: string } }
|
||||
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AIHintPack" }
|
||||
"304": { description: The caller already has this pack version }
|
||||
"404": { description: Locale is unsupported or has not been published }
|
||||
/hints/manifest.json:
|
||||
get:
|
||||
security: []
|
||||
summary: Return the AI Hint manifest at the legacy file path
|
||||
parameters:
|
||||
- name: If-None-Match
|
||||
in: header
|
||||
schema: { type: string }
|
||||
responses:
|
||||
"200":
|
||||
description: Same payload and cache validators as /v1/content/hints/manifest
|
||||
headers:
|
||||
ETag: { schema: { type: string } }
|
||||
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AIHintManifest" }
|
||||
"304": { description: The caller already has the current manifest }
|
||||
/hints/hints-{locale}.json:
|
||||
get:
|
||||
security: []
|
||||
summary: Return an AI Hint pack at the legacy file path
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/HintLocale"
|
||||
- name: If-None-Match
|
||||
in: header
|
||||
schema: { type: string }
|
||||
responses:
|
||||
"200":
|
||||
description: Same payload and cache validators as /v1/content/hints/{locale}
|
||||
headers:
|
||||
ETag: { schema: { type: string } }
|
||||
Cache-Control: { schema: { type: string, const: "public,max-age=300" } }
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AIHintPack" }
|
||||
"304": { description: The caller already has this pack version }
|
||||
"404": { description: Locale is unsupported or has not been published }
|
||||
/.well-known/apple-app-site-association:
|
||||
get:
|
||||
servers:
|
||||
@@ -522,6 +643,125 @@ paths:
|
||||
"200": { description: Bilingual HTML landing page }
|
||||
"404": { description: Invalid, unknown, or expired invitation }
|
||||
"503": { description: Invitation lookup is temporarily unavailable }
|
||||
/v1/admin/content/skills:
|
||||
get:
|
||||
security:
|
||||
- adminMtls: []
|
||||
adminSession: []
|
||||
summary: List all official Skills including disabled entries
|
||||
responses:
|
||||
"200":
|
||||
description: Administrative Skill catalog
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AdminOfficialSkillCatalog" }
|
||||
"403": { description: SUPER_ADMIN or SUPPORT role is required }
|
||||
post:
|
||||
security:
|
||||
- adminMtls: []
|
||||
adminSession: []
|
||||
summary: Create a disabled official Skill
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/AdminCsrf"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/CreateOfficialSkillRequest" }
|
||||
responses:
|
||||
"201":
|
||||
description: Skill created
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AdminOfficialSkill" }
|
||||
"400": { description: Request is invalid }
|
||||
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
||||
"409": { description: Skill ID already exists }
|
||||
/v1/admin/content/skills/{id}:
|
||||
put:
|
||||
security:
|
||||
- adminMtls: []
|
||||
adminSession: []
|
||||
summary: Update an official Skill and increment catalog revision
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/OfficialSkillId"
|
||||
- $ref: "#/components/parameters/AdminCsrf"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/UpdateOfficialSkillRequest" }
|
||||
responses:
|
||||
"200":
|
||||
description: Skill updated
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AdminOfficialSkill" }
|
||||
"400": { description: Request is invalid }
|
||||
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
||||
"404": { description: Skill was not found }
|
||||
/v1/admin/content/skills/{id}/enable:
|
||||
post:
|
||||
security:
|
||||
- adminMtls: []
|
||||
adminSession: []
|
||||
summary: Enable an official Skill and increment catalog revision
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/OfficialSkillId"
|
||||
- $ref: "#/components/parameters/AdminCsrf"
|
||||
responses:
|
||||
"204": { description: Skill enabled }
|
||||
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
||||
"404": { description: Skill was not found }
|
||||
/v1/admin/content/skills/{id}/disable:
|
||||
post:
|
||||
security:
|
||||
- adminMtls: []
|
||||
adminSession: []
|
||||
summary: Disable an official Skill and increment catalog revision
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/OfficialSkillId"
|
||||
- $ref: "#/components/parameters/AdminCsrf"
|
||||
responses:
|
||||
"204": { description: Skill disabled }
|
||||
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
||||
"404": { description: Skill was not found }
|
||||
/v1/admin/content/hints/{locale}:
|
||||
get:
|
||||
security:
|
||||
- adminMtls: []
|
||||
adminSession: []
|
||||
summary: Return a locale Hint pack for editing
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/HintLocale"
|
||||
responses:
|
||||
"200":
|
||||
description: Existing pack or an empty version-zero editor document
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AdminAIHintPack" }
|
||||
"403": { description: SUPER_ADMIN or SUPPORT role is required }
|
||||
put:
|
||||
security:
|
||||
- adminMtls: []
|
||||
adminSession: []
|
||||
summary: Immediately publish a locale Hint pack and increment its version
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/HintLocale"
|
||||
- $ref: "#/components/parameters/AdminCsrf"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/UpdateAIHintPackRequest" }
|
||||
responses:
|
||||
"200":
|
||||
description: Published pack
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: "#/components/schemas/AdminAIHintPack" }
|
||||
"400": { description: Pack is invalid }
|
||||
"403": { description: SUPER_ADMIN role and valid CSRF are required }
|
||||
/v1/admin/auth/session:
|
||||
get:
|
||||
security:
|
||||
@@ -941,6 +1181,11 @@ paths:
|
||||
- OPERATOR_CREDENTIALS_RESET
|
||||
- OPERATOR_SESSIONS_REVOKED
|
||||
- MANUAL_CREDIT_GRANTED
|
||||
- CONTENT_SKILL_CREATED
|
||||
- CONTENT_SKILL_UPDATED
|
||||
- CONTENT_SKILL_ENABLED
|
||||
- CONTENT_SKILL_DISABLED
|
||||
- CONTENT_HINT_PACK_PUBLISHED
|
||||
- name: result
|
||||
in: query
|
||||
schema: { type: string, enum: [success, rejected] }
|
||||
@@ -982,6 +1227,19 @@ components:
|
||||
in: header
|
||||
required: true
|
||||
schema: { type: string, minLength: 8, maxLength: 128 }
|
||||
OfficialSkillId:
|
||||
name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
maxLength: 100
|
||||
pattern: "^official\\.[a-z0-9._-]+$"
|
||||
HintLocale:
|
||||
name: locale
|
||||
in: path
|
||||
required: true
|
||||
schema: { type: string, enum: [zh, en] }
|
||||
AdminCsrf:
|
||||
name: X-CSRF-Token
|
||||
in: header
|
||||
@@ -1125,6 +1383,55 @@ components:
|
||||
minItems: 1
|
||||
maxItems: 50
|
||||
items: { $ref: "#/components/schemas/ProductAnalyticsEvent" }
|
||||
KeyboardUsageSummary:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- clientSummaryId
|
||||
- summaryDate
|
||||
- chineseCharacterCount
|
||||
- englishCharacterCount
|
||||
- otherCharacterCount
|
||||
- inputSessionCount
|
||||
- chineseOnlySessionCount
|
||||
- englishOnlySessionCount
|
||||
- mixedLanguageSessionCount
|
||||
- otherOnlySessionCount
|
||||
properties:
|
||||
clientSummaryId: { type: string, format: uuid }
|
||||
summaryDate:
|
||||
type: string
|
||||
format: date
|
||||
description: Finalized UTC date; accepted from 35 days ago through yesterday.
|
||||
chineseCharacterCount: { type: integer, format: int64, minimum: 0, maximum: 1000000 }
|
||||
englishCharacterCount: { type: integer, format: int64, minimum: 0, maximum: 1000000 }
|
||||
otherCharacterCount: { type: integer, format: int64, minimum: 0, maximum: 1000000 }
|
||||
inputSessionCount: { type: integer, format: int64, minimum: 1, maximum: 100000 }
|
||||
chineseOnlySessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
||||
englishOnlySessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
||||
mixedLanguageSessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
||||
otherOnlySessionCount: { type: integer, format: int64, minimum: 0, maximum: 100000 }
|
||||
appVersion:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 32
|
||||
pattern: "^[A-Za-z0-9._+-]+$"
|
||||
osVersion:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 32
|
||||
pattern: "^[A-Za-z0-9._+-]+$"
|
||||
KeyboardUsageBatchRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [installationId, summaries]
|
||||
properties:
|
||||
installationId: { type: string, format: uuid }
|
||||
summaries:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 50
|
||||
items: { $ref: "#/components/schemas/KeyboardUsageSummary" }
|
||||
ProductAnalyticsBatchResponse:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
@@ -1132,6 +1439,172 @@ components:
|
||||
properties:
|
||||
accepted: { type: integer, minimum: 0, maximum: 50 }
|
||||
replayed: { type: integer, minimum: 0, maximum: 50 }
|
||||
SkillLocalization:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [name, summary, prompt]
|
||||
properties:
|
||||
name: { type: string, minLength: 1, maxLength: 40 }
|
||||
summary: { type: string, minLength: 1, maxLength: 200 }
|
||||
prompt: { type: string, minLength: 1, maxLength: 6000 }
|
||||
SkillLocalizations:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [zh-Hans, en]
|
||||
properties:
|
||||
zh-Hans: { $ref: "#/components/schemas/SkillLocalization" }
|
||||
en: { $ref: "#/components/schemas/SkillLocalization" }
|
||||
OfficialSkill:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [id, systemImage, sortOrder, kind, thinkingEnabled, localizations]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
maxLength: 100
|
||||
pattern: "^official\\.[a-z0-9._-]+$"
|
||||
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
||||
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
||||
kind: { type: string, const: transform }
|
||||
thinkingEnabled: { type: boolean }
|
||||
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
||||
AdminOfficialSkill:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [id, systemImage, sortOrder, kind, thinkingEnabled, enabled, localizations]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
maxLength: 100
|
||||
pattern: "^official\\.[a-z0-9._-]+$"
|
||||
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
||||
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
||||
kind: { type: string, const: transform }
|
||||
thinkingEnabled: { type: boolean }
|
||||
enabled: { type: boolean }
|
||||
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
||||
OfficialSkillCatalog:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [schemaVersion, revision, skills]
|
||||
properties:
|
||||
schemaVersion: { type: integer, const: 1 }
|
||||
revision: { type: integer, format: int64, minimum: 0 }
|
||||
generatedAt: { type: string, format: date-time }
|
||||
skills:
|
||||
type: array
|
||||
maxItems: 100
|
||||
items: { $ref: "#/components/schemas/OfficialSkill" }
|
||||
AdminOfficialSkillCatalog:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [revision, skills]
|
||||
properties:
|
||||
revision: { type: integer, format: int64, minimum: 0 }
|
||||
generatedAt: { type: string, format: date-time }
|
||||
skills:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/AdminOfficialSkill" }
|
||||
CreateOfficialSkillRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [id, systemImage, sortOrder, thinkingEnabled, localizations]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
maxLength: 100
|
||||
pattern: "^official\\.[a-z0-9._-]+$"
|
||||
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
||||
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
||||
thinkingEnabled: { type: boolean }
|
||||
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
||||
UpdateOfficialSkillRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [systemImage, sortOrder, thinkingEnabled, localizations]
|
||||
properties:
|
||||
systemImage: { type: string, minLength: 1, maxLength: 100 }
|
||||
sortOrder: { type: integer, minimum: 0, maximum: 100000 }
|
||||
thinkingEnabled: { type: boolean }
|
||||
localizations: { $ref: "#/components/schemas/SkillLocalizations" }
|
||||
AIHintCard:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [id, prompt, category, priority, source, locale, conditions]
|
||||
anyOf:
|
||||
- required: [displayText]
|
||||
- required: [text]
|
||||
properties:
|
||||
id: { type: string, minLength: 1, maxLength: 128 }
|
||||
displayText: { type: string, minLength: 1, maxLength: 500 }
|
||||
text: { type: string, minLength: 1, maxLength: 500 }
|
||||
prompt: { type: string, minLength: 1, maxLength: 16000 }
|
||||
category: { type: string, minLength: 1, maxLength: 64 }
|
||||
priority: { type: integer, minimum: -10000, maximum: 10000 }
|
||||
source: { type: string, minLength: 1, maxLength: 64 }
|
||||
locale: { type: string, enum: [zh, en] }
|
||||
conditions:
|
||||
type: array
|
||||
maxItems: 20
|
||||
items: { type: string, minLength: 1, maxLength: 64 }
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
AIHintPack:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [locale, version, cards]
|
||||
properties:
|
||||
locale: { type: string, enum: [zh, en] }
|
||||
generatedAt: { type: string, format: date-time }
|
||||
expiresAt: { type: string, format: date-time }
|
||||
version: { type: integer, minimum: 1 }
|
||||
cards:
|
||||
type: array
|
||||
maxItems: 500
|
||||
items: { $ref: "#/components/schemas/AIHintCard" }
|
||||
AdminAIHintPack:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [locale, version, cards]
|
||||
properties:
|
||||
locale: { type: string, enum: [zh, en] }
|
||||
generatedAt: { type: string, format: date-time }
|
||||
expiresAt: { type: string, format: date-time }
|
||||
intervalHours: { type: integer, minimum: 1, maximum: 168 }
|
||||
version: { type: integer, minimum: 0 }
|
||||
cards:
|
||||
type: array
|
||||
maxItems: 500
|
||||
items: { $ref: "#/components/schemas/AIHintCard" }
|
||||
UpdateAIHintPackRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [cards]
|
||||
properties:
|
||||
generatedAt: { type: string, format: date-time }
|
||||
expiresAt: { type: string, format: date-time }
|
||||
intervalHours: { type: integer, minimum: 1, maximum: 168 }
|
||||
cards:
|
||||
type: array
|
||||
maxItems: 500
|
||||
items: { $ref: "#/components/schemas/AIHintCard" }
|
||||
AIHintManifest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [locales, files]
|
||||
properties:
|
||||
generatedAt: { type: string, format: date-time }
|
||||
expiresAt: { type: string, format: date-time }
|
||||
intervalHours: { type: integer, minimum: 1, maximum: 168 }
|
||||
locales:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
items: { type: string, enum: [zh, en] }
|
||||
files:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: [string, "null"]
|
||||
AdminSessionState:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
@@ -1286,6 +1759,42 @@ components:
|
||||
executionMode: { type: string, enum: [MANAGED, LOCAL, BYOK] }
|
||||
users: { type: integer, format: int64, minimum: 0 }
|
||||
successes: { type: integer, format: int64, minimum: 0 }
|
||||
AdminAnalyticsKeyboardUsage:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- activeUsers
|
||||
- activationToInput
|
||||
- chineseActiveUsers
|
||||
- englishActiveUsers
|
||||
- bilingualActiveUsers
|
||||
- totalCharacters
|
||||
- chineseCharacters
|
||||
- englishCharacters
|
||||
- otherCharacters
|
||||
- inputSessions
|
||||
- chineseOnlySessions
|
||||
- englishOnlySessions
|
||||
- mixedLanguageSessions
|
||||
- otherOnlySessions
|
||||
properties:
|
||||
activeUsers: { type: integer, format: int64, minimum: 0 }
|
||||
activationToInput: { $ref: "#/components/schemas/AdminAnalyticsRate" }
|
||||
chineseActiveUsers: { type: integer, format: int64, minimum: 0 }
|
||||
englishActiveUsers: { type: integer, format: int64, minimum: 0 }
|
||||
bilingualActiveUsers: { type: integer, format: int64, minimum: 0 }
|
||||
totalCharacters: { type: integer, format: int64, minimum: 0 }
|
||||
chineseCharacters: { type: integer, format: int64, minimum: 0 }
|
||||
englishCharacters: { type: integer, format: int64, minimum: 0 }
|
||||
otherCharacters: { type: integer, format: int64, minimum: 0 }
|
||||
chineseSharePercent: { type: ["number", "null"], minimum: 0, maximum: 100 }
|
||||
englishSharePercent: { type: ["number", "null"], minimum: 0, maximum: 100 }
|
||||
inputSessions: { type: integer, format: int64, minimum: 0 }
|
||||
averageCharactersPerInputSession: { type: ["number", "null"], minimum: 0 }
|
||||
chineseOnlySessions: { type: integer, format: int64, minimum: 0 }
|
||||
englishOnlySessions: { type: integer, format: int64, minimum: 0 }
|
||||
mixedLanguageSessions: { type: integer, format: int64, minimum: 0 }
|
||||
otherOnlySessions: { type: integer, format: int64, minimum: 0 }
|
||||
AdminProductAnalytics:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
@@ -1299,6 +1808,7 @@ components:
|
||||
- growthFunnel
|
||||
- retention
|
||||
- aiFeatures
|
||||
- keyboardUsage
|
||||
- referralFunnel
|
||||
- guardrails
|
||||
properties:
|
||||
@@ -1370,6 +1880,8 @@ components:
|
||||
aiFeatures:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/AdminAnalyticsFeatureUsage" }
|
||||
keyboardUsage:
|
||||
$ref: "#/components/schemas/AdminAnalyticsKeyboardUsage"
|
||||
referralFunnel:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/AdminAnalyticsFunnelStep" }
|
||||
|
||||
Reference in New Issue
Block a user