mirror of
https://github.com/hanzoai/insights.git
synced 2026-11-02 14:32:45 +00:00
Every first-party Python caller and test now names the mount the API actually answers on. Mechanical: URL literals only, and only where the /api/ was ours -- a preceding word character means it was a module path like insights/api/ or a third-party host like slack.com/api/, and those are untouched. Left saying /api/, each for a reason rather than for safety: - products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints - services/llm-gateway deploys separately and is under a code freeze, so moving its two calls would break it during the skew - the mcp_store and Slack/Teams redirect_uris are registered with third parties and must byte-match what was registered - the Wizard and Raycast client-metadata paths are OAuth client_ids - /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the ingress routes to the Rust fleets, held by released SDK versions Test URLconfs moved with the requests that call them, and the Datastore user-selection fixtures now carry the leading slash that request.path has.
213 lines
6.8 KiB
TypeScript
213 lines
6.8 KiB
TypeScript
import { MakeLogicType, actions, connect, kea, listeners, path, reducers, selectors } from 'kea'
|
|
|
|
import api from 'lib/api'
|
|
import { teamLogic } from 'scenes/teamLogic'
|
|
|
|
import { LLMTracePerson } from '~/queries/schema/schema-general'
|
|
|
|
interface PersonAPIResponse {
|
|
uuid: string
|
|
name: string
|
|
distinct_ids: string[]
|
|
properties: Record<string, unknown>
|
|
created_at: string
|
|
}
|
|
|
|
interface BatchByDistinctIdsResponse {
|
|
results: Record<string, PersonAPIResponse>
|
|
}
|
|
|
|
function toTracePerson(apiPerson: PersonAPIResponse, distinctId: string): LLMTracePerson {
|
|
return {
|
|
uuid: apiPerson.uuid,
|
|
created_at: apiPerson.created_at,
|
|
properties: apiPerson.properties,
|
|
distinct_id: distinctId,
|
|
}
|
|
}
|
|
|
|
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
|
|
export interface llmPersonsLazyLoaderLogicValues {
|
|
currentTeamId: number | null // teamLogic
|
|
isDistinctIdLoading: (distinctId: string) => boolean
|
|
loadingDistinctIds: Set<string>
|
|
personsCache: Record<string, LLMTracePerson | null>
|
|
}
|
|
|
|
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
|
|
export interface llmPersonsLazyLoaderLogicActions {
|
|
ensurePersonLoaded: (distinctId: string) => {
|
|
distinctId: string
|
|
}
|
|
loadPersonsBatchFailure: (requestedDistinctIds: string[]) => {
|
|
requestedDistinctIds: string[]
|
|
}
|
|
loadPersonsBatchSuccess: (
|
|
persons: Record<string, LLMTracePerson>,
|
|
requestedDistinctIds: string[]
|
|
) => {
|
|
persons: Record<string, LLMTracePerson>
|
|
requestedDistinctIds: string[]
|
|
}
|
|
queuePersonLoad: (distinctId: string) => {
|
|
distinctId: string
|
|
}
|
|
}
|
|
|
|
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
|
|
export interface llmPersonsLazyLoaderLogicMeta {
|
|
__keaTypeGenInternalSelectorTypes: {
|
|
isDistinctIdLoading: (loadingDistinctIds: Set<string>) => (distinctId: string) => boolean
|
|
}
|
|
}
|
|
|
|
export type llmPersonsLazyLoaderLogicType = MakeLogicType<
|
|
llmPersonsLazyLoaderLogicValues,
|
|
llmPersonsLazyLoaderLogicActions,
|
|
Record<string, any>,
|
|
llmPersonsLazyLoaderLogicMeta
|
|
>
|
|
|
|
export const llmPersonsLazyLoaderLogic = kea<llmPersonsLazyLoaderLogicType>([
|
|
path(['products', 'ai_observability', 'frontend', 'llmPersonsLazyLoaderLogic']),
|
|
|
|
connect({
|
|
values: [teamLogic, ['currentTeamId']],
|
|
}),
|
|
|
|
actions({
|
|
ensurePersonLoaded: (distinctId: string) => ({ distinctId }),
|
|
queuePersonLoad: (distinctId: string) => ({ distinctId }),
|
|
loadPersonsBatchSuccess: (persons: Record<string, LLMTracePerson>, requestedDistinctIds: string[]) => ({
|
|
persons,
|
|
requestedDistinctIds,
|
|
}),
|
|
loadPersonsBatchFailure: (requestedDistinctIds: string[]) => ({ requestedDistinctIds }),
|
|
}),
|
|
|
|
reducers({
|
|
personsCache: [
|
|
{} as Record<string, LLMTracePerson | null>,
|
|
{
|
|
loadPersonsBatchSuccess: (state, { persons, requestedDistinctIds }) => {
|
|
const newState = { ...state }
|
|
|
|
for (const distinctId of requestedDistinctIds) {
|
|
newState[distinctId] = persons[distinctId] ?? null
|
|
}
|
|
|
|
return newState
|
|
},
|
|
loadPersonsBatchFailure: (state, { requestedDistinctIds }) => {
|
|
const newState = { ...state }
|
|
|
|
for (const distinctId of requestedDistinctIds) {
|
|
newState[distinctId] = null
|
|
}
|
|
|
|
return newState
|
|
},
|
|
},
|
|
],
|
|
|
|
loadingDistinctIds: [
|
|
new Set<string>(),
|
|
{
|
|
queuePersonLoad: (state, { distinctId }) => {
|
|
if (state.has(distinctId)) {
|
|
return state
|
|
}
|
|
|
|
const newSet = new Set(state)
|
|
newSet.add(distinctId)
|
|
return newSet
|
|
},
|
|
loadPersonsBatchSuccess: (state, { requestedDistinctIds }) => {
|
|
const newSet = new Set(state)
|
|
|
|
for (const id of requestedDistinctIds) {
|
|
newSet.delete(id)
|
|
}
|
|
|
|
return newSet
|
|
},
|
|
loadPersonsBatchFailure: (state, { requestedDistinctIds }) => {
|
|
const newSet = new Set(state)
|
|
|
|
for (const id of requestedDistinctIds) {
|
|
newSet.delete(id)
|
|
}
|
|
|
|
return newSet
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
|
|
selectors({
|
|
isDistinctIdLoading: [
|
|
(s) => [s.loadingDistinctIds],
|
|
(loadingDistinctIds: Set<string>): ((distinctId: string) => boolean) => {
|
|
return (distinctId: string) => loadingDistinctIds.has(distinctId)
|
|
},
|
|
],
|
|
}),
|
|
|
|
listeners(({ values, actions }) => {
|
|
let pendingDistinctIds = new Set<string>()
|
|
let batchTimer: ReturnType<typeof setTimeout> | null = null
|
|
const scheduleBatchLoad = (): void => {
|
|
if (batchTimer) {
|
|
return
|
|
}
|
|
|
|
batchTimer = setTimeout(async () => {
|
|
const batch = Array.from(pendingDistinctIds)
|
|
pendingDistinctIds = new Set()
|
|
batchTimer = null
|
|
|
|
if (batch.length === 0) {
|
|
return
|
|
}
|
|
|
|
const teamId = values.currentTeamId
|
|
|
|
if (!teamId) {
|
|
actions.loadPersonsBatchFailure(batch)
|
|
return
|
|
}
|
|
|
|
try {
|
|
// nosemgrep: prefer-codegen-api
|
|
const response = await api.create<BatchByDistinctIdsResponse>(
|
|
`v1/environments/${teamId}/persons/batch_by_distinct_ids/`,
|
|
{ distinct_ids: batch }
|
|
)
|
|
|
|
const persons: Record<string, LLMTracePerson> = {}
|
|
|
|
for (const [distinctId, personData] of Object.entries(response.results)) {
|
|
persons[distinctId] = toTracePerson(personData, distinctId)
|
|
}
|
|
|
|
actions.loadPersonsBatchSuccess(persons, batch)
|
|
} catch {
|
|
actions.loadPersonsBatchFailure(batch)
|
|
}
|
|
}, 0)
|
|
}
|
|
|
|
return {
|
|
ensurePersonLoaded: ({ distinctId }) => {
|
|
if (values.personsCache[distinctId] !== undefined) {
|
|
return
|
|
}
|
|
|
|
actions.queuePersonLoad(distinctId)
|
|
pendingDistinctIds.add(distinctId)
|
|
scheduleBatchLoad()
|
|
},
|
|
}
|
|
}),
|
|
])
|