mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
542 lines
20 KiB
TypeScript
542 lines
20 KiB
TypeScript
import { MakeLogicType, actions, connect, kea, listeners, path, reducers } from 'kea'
|
|
import { loaders } from 'kea-loaders'
|
|
import { router } from 'kea-router'
|
|
|
|
import api from 'lib/api'
|
|
import { SetupTaskId, globalSetupLogic } from 'lib/components/ProductSetup'
|
|
import { Dialog } from 'lib/elements/Dialog'
|
|
import { toast } from 'lib/elements/Toast/Toast'
|
|
import { debounce } from 'lib/utils/async'
|
|
import { permanentlyMount } from 'lib/utils/kea-logic-builders'
|
|
import { slugify } from 'lib/utils/strings'
|
|
import { teamLogic } from 'scenes/teamLogic'
|
|
import { urls } from 'scenes/urls'
|
|
|
|
import { sceneLayoutLogic } from '~/layout/scenes/sceneLayoutLogic'
|
|
import { EndpointRequest, ProductIntentContext, ProductKey } from '~/queries/schema/schema-general'
|
|
import { EndpointType, EndpointVersionType } from '~/types'
|
|
|
|
import type { ProductIntentProperties } from '../../../frontend/src/lib/utils/product-intents'
|
|
import type { EndpointVersionMaterializationType } from '../../../frontend/src/types'
|
|
import { endpointsLogic } from './endpointsLogic'
|
|
import { insightPickerEndpointModalLogic } from './insightPickerEndpointModalLogic'
|
|
|
|
export type CodeExampleTab = 'terminal' | 'python' | 'nodejs'
|
|
|
|
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
|
|
export interface endpointLogicValues {
|
|
activeCodeExampleTab: CodeExampleTab
|
|
createFromInsightModalOpen: boolean
|
|
duplicateEndpoint: EndpointType | null
|
|
endpoint: EndpointVersionType | null
|
|
endpointDescription: string | null
|
|
endpointLoading: boolean
|
|
endpointName: string | null
|
|
isUpdateMode: boolean
|
|
materializationStatus: EndpointType['materialization'] | null
|
|
materializationStatusLoading: boolean
|
|
selectedCodeExampleVersion: number | null
|
|
selectedEndpointName: string | null
|
|
updatingEndpoint: boolean
|
|
versions: EndpointVersionType[]
|
|
versionsLoading: boolean
|
|
}
|
|
|
|
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
|
|
export interface endpointLogicActions {
|
|
loadEndpoints: () => any // endpointsLogic
|
|
setScenePanelOpen: (open: boolean) => {
|
|
open: boolean
|
|
} // sceneLayoutLogic
|
|
addProductIntent: (properties: ProductIntentProperties) => ProductIntentProperties // teamLogic
|
|
clearMaterializationStatus: () => {
|
|
value: true
|
|
}
|
|
closeCreateFromInsightModal: () => {
|
|
value: true
|
|
}
|
|
confirmToggleActive: (endpoint: EndpointType) => {
|
|
endpoint: EndpointType
|
|
}
|
|
createEndpoint: (request: EndpointRequest) => {
|
|
request: EndpointRequest
|
|
}
|
|
createEndpointFailure: (queryError?: string | null) => {
|
|
queryError: string | null | undefined
|
|
}
|
|
createEndpointSuccess: (response: any) => {
|
|
response: any
|
|
}
|
|
deleteEndpoint: (name: string) => {
|
|
name: string
|
|
}
|
|
deleteEndpointFailure: () => {}
|
|
deleteEndpointSuccess: (response: any) => {
|
|
response: any
|
|
}
|
|
loadEndpoint: (name: string) => string
|
|
loadEndpointFailure: (
|
|
error: string,
|
|
errorObject?: any
|
|
) => {
|
|
error: string
|
|
errorObject?: any
|
|
}
|
|
loadEndpointSuccess: (
|
|
endpoint: EndpointVersionType | null,
|
|
payload?: string
|
|
) => {
|
|
endpoint: EndpointVersionType | null
|
|
payload?: string
|
|
}
|
|
loadMaterializationStatus: ({ name, version }: { name: string; version?: number }) => {
|
|
name: string
|
|
version?: number
|
|
}
|
|
loadMaterializationStatusFailure: (
|
|
error: string,
|
|
errorObject?: any
|
|
) => {
|
|
error: string
|
|
errorObject?: any
|
|
}
|
|
loadMaterializationStatusSuccess: (
|
|
materializationStatus: EndpointVersionMaterializationType | null | undefined,
|
|
payload?: {
|
|
name: string
|
|
version?: number
|
|
}
|
|
) => {
|
|
materializationStatus: EndpointVersionMaterializationType | null | undefined
|
|
payload?: {
|
|
name: string
|
|
version?: number
|
|
}
|
|
}
|
|
loadVersions: (name: string) => string
|
|
loadVersionsFailure: (
|
|
error: string,
|
|
errorObject?: any
|
|
) => {
|
|
error: string
|
|
errorObject?: any
|
|
}
|
|
loadVersionsSuccess: (
|
|
versions: EndpointVersionType[],
|
|
payload?: string
|
|
) => {
|
|
versions: EndpointVersionType[]
|
|
payload?: string
|
|
}
|
|
openCreateFromInsightModal: () => {
|
|
value: true
|
|
}
|
|
saveTagsInline: (tags: string[]) => {
|
|
tags: string[]
|
|
}
|
|
setActiveCodeExampleTab: (tab: CodeExampleTab) => {
|
|
tab: CodeExampleTab
|
|
}
|
|
setDuplicateEndpoint: (endpoint: EndpointType | null) => {
|
|
endpoint: EndpointType | null
|
|
}
|
|
setEndpointDescription: (endpointDescription: string | null) => {
|
|
endpointDescription: string | null
|
|
}
|
|
setEndpointName: (endpointName: string) => {
|
|
endpointName: string
|
|
}
|
|
setIsUpdateMode: (isUpdateMode: boolean) => {
|
|
isUpdateMode: boolean
|
|
}
|
|
setSelectedCodeExampleVersion: (version: number | null) => {
|
|
version: number | null
|
|
}
|
|
setSelectedEndpointName: (selectedEndpointName: string | null) => {
|
|
selectedEndpointName: string | null
|
|
}
|
|
updateEndpoint: (
|
|
name: string,
|
|
request: Partial<EndpointRequest>,
|
|
options?: {
|
|
showViewButton?: boolean
|
|
version?: number
|
|
}
|
|
) => {
|
|
name: string
|
|
options:
|
|
| {
|
|
showViewButton?: boolean | undefined
|
|
version?: number | undefined
|
|
}
|
|
| undefined
|
|
request: Partial<EndpointRequest>
|
|
}
|
|
updateEndpointFailure: (queryError?: string | null) => {
|
|
queryError: string | null | undefined
|
|
}
|
|
updateEndpointSuccess: (
|
|
response: any,
|
|
endpointName: string,
|
|
options?: {
|
|
showViewButton?: boolean
|
|
version?: number
|
|
}
|
|
) => {
|
|
endpointName: string
|
|
options:
|
|
| {
|
|
showViewButton?: boolean | undefined
|
|
version?: number | undefined
|
|
}
|
|
| undefined
|
|
response: any
|
|
}
|
|
}
|
|
|
|
export type endpointLogicType = MakeLogicType<endpointLogicValues, endpointLogicActions>
|
|
|
|
export const endpointLogic = kea<endpointLogicType>([
|
|
path(['products', 'endpoints', 'frontend', 'endpointLogic']),
|
|
connect(() => ({
|
|
actions: [
|
|
endpointsLogic,
|
|
['loadEndpoints'],
|
|
sceneLayoutLogic,
|
|
['setScenePanelOpen'],
|
|
teamLogic,
|
|
['addProductIntent'],
|
|
],
|
|
})),
|
|
actions({
|
|
setEndpointName: (endpointName: string) => ({ endpointName }),
|
|
setEndpointDescription: (endpointDescription: string | null) => ({ endpointDescription }),
|
|
setActiveCodeExampleTab: (tab: CodeExampleTab) => ({ tab }),
|
|
setSelectedCodeExampleVersion: (version: number | null) => ({ version }),
|
|
setIsUpdateMode: (isUpdateMode: boolean) => ({ isUpdateMode }),
|
|
setSelectedEndpointName: (selectedEndpointName: string | null) => ({ selectedEndpointName }),
|
|
openCreateFromInsightModal: true,
|
|
closeCreateFromInsightModal: true,
|
|
setDuplicateEndpoint: (endpoint: EndpointType | null) => ({ endpoint }),
|
|
createEndpoint: (request: EndpointRequest) => ({ request }),
|
|
createEndpointSuccess: (response: any) => ({ response }),
|
|
createEndpointFailure: (queryError?: string | null) => ({ queryError }),
|
|
updateEndpoint: (
|
|
name: string,
|
|
request: Partial<EndpointRequest>,
|
|
options?: { showViewButton?: boolean; version?: number }
|
|
) => ({
|
|
name,
|
|
request,
|
|
options,
|
|
}),
|
|
updateEndpointSuccess: (
|
|
response: any,
|
|
endpointName: string,
|
|
options?: { showViewButton?: boolean; version?: number }
|
|
) => ({
|
|
response,
|
|
endpointName,
|
|
options,
|
|
}),
|
|
updateEndpointFailure: (queryError?: string | null) => ({ queryError }),
|
|
deleteEndpoint: (name: string) => ({ name }),
|
|
deleteEndpointSuccess: (response: any) => ({ response }),
|
|
clearMaterializationStatus: true,
|
|
deleteEndpointFailure: () => ({}),
|
|
confirmToggleActive: (endpoint: EndpointType) => ({ endpoint }),
|
|
saveTagsInline: (tags: string[]) => ({ tags }),
|
|
}),
|
|
reducers({
|
|
endpointName: [null as string | null, { setEndpointName: (_, { endpointName }) => endpointName }],
|
|
endpointDescription: [
|
|
null as string | null,
|
|
{ setEndpointDescription: (_, { endpointDescription }) => endpointDescription },
|
|
],
|
|
activeCodeExampleTab: ['terminal' as CodeExampleTab, { setActiveCodeExampleTab: (_, { tab }) => tab }],
|
|
selectedCodeExampleVersion: [
|
|
null as number | null,
|
|
{ setSelectedCodeExampleVersion: (_, { version }) => version },
|
|
],
|
|
isUpdateMode: [
|
|
false,
|
|
{
|
|
setIsUpdateMode: (_, { isUpdateMode }) => isUpdateMode,
|
|
},
|
|
],
|
|
selectedEndpointName: [
|
|
null as string | null,
|
|
{
|
|
setSelectedEndpointName: (_, { selectedEndpointName }) => selectedEndpointName,
|
|
},
|
|
],
|
|
createFromInsightModalOpen: [
|
|
false,
|
|
{
|
|
openCreateFromInsightModal: () => true,
|
|
setDuplicateEndpoint: (_, { endpoint }) => !!endpoint,
|
|
closeCreateFromInsightModal: () => false,
|
|
createEndpointSuccess: () => false,
|
|
},
|
|
],
|
|
duplicateEndpoint: [
|
|
null as EndpointType | null,
|
|
{
|
|
setDuplicateEndpoint: (_, { endpoint }) => endpoint,
|
|
createEndpointSuccess: () => null,
|
|
},
|
|
],
|
|
updatingEndpoint: [
|
|
false,
|
|
{
|
|
updateEndpoint: () => true,
|
|
updateEndpointSuccess: () => false,
|
|
updateEndpointFailure: () => false,
|
|
},
|
|
],
|
|
// Clear stale endpoint data immediately when loading a new endpoint
|
|
endpoint: [
|
|
null as EndpointVersionType | null,
|
|
{
|
|
loadEndpoint: () => null,
|
|
},
|
|
],
|
|
versions: [
|
|
[] as EndpointVersionType[],
|
|
{
|
|
loadEndpoint: () => [],
|
|
},
|
|
],
|
|
// Extend the loader reducer to clear on action
|
|
materializationStatus: [
|
|
null as EndpointType['materialization'] | null,
|
|
{
|
|
clearMaterializationStatus: () => null,
|
|
},
|
|
],
|
|
}),
|
|
loaders(({ actions, values }) => ({
|
|
endpoint: [
|
|
null as EndpointVersionType | null,
|
|
{
|
|
loadEndpoint: async (name: string) => {
|
|
if (!name) {
|
|
return null
|
|
}
|
|
return await api.endpoint.get(name)
|
|
},
|
|
},
|
|
],
|
|
materializationStatus: [
|
|
null as EndpointType['materialization'] | null,
|
|
{
|
|
loadMaterializationStatus: async ({ name, version }: { name: string; version?: number }) => {
|
|
if (!name) {
|
|
return null
|
|
}
|
|
const materializationStatus = await api.endpoint.getMaterializationStatus(name, version)
|
|
|
|
// Update the endpoint object with the new materialization status (only for current version)
|
|
if (values.endpoint && version === undefined) {
|
|
const updatedEndpoint = {
|
|
...values.endpoint,
|
|
materialization: materializationStatus,
|
|
is_materialized: materializationStatus?.can_materialize
|
|
? !!materializationStatus.status
|
|
: false,
|
|
}
|
|
actions.loadEndpointSuccess(updatedEndpoint)
|
|
}
|
|
|
|
return materializationStatus
|
|
},
|
|
},
|
|
],
|
|
versions: [
|
|
[] as EndpointVersionType[],
|
|
{
|
|
loadVersions: async (name: string) => {
|
|
if (!name) {
|
|
return []
|
|
}
|
|
const response = await api.endpoint.listVersions(name)
|
|
return response.results
|
|
},
|
|
},
|
|
],
|
|
})),
|
|
listeners(({ actions, values }) => {
|
|
const reloadMaterializationStatus = debounce((name: string, version?: number): void => {
|
|
actions.loadMaterializationStatus({ name, version })
|
|
}, 2000)
|
|
return {
|
|
openCreateFromInsightModal: () => {
|
|
actions.loadEndpoints()
|
|
},
|
|
closeCreateFromInsightModal: () => {
|
|
actions.setDuplicateEndpoint(null)
|
|
insightPickerEndpointModalLogic.findMounted()?.actions.clearSelectedInsight()
|
|
},
|
|
createEndpoint: async ({ request }) => {
|
|
try {
|
|
if (request.name) {
|
|
request.name = slugify(request.name)
|
|
}
|
|
const response = await api.endpoint.create(request)
|
|
actions.createEndpointSuccess(response)
|
|
} catch (error: any) {
|
|
console.error('Failed to create endpoint:', error)
|
|
actions.createEndpointFailure(error.detail || null)
|
|
}
|
|
},
|
|
createEndpointSuccess: ({ response }) => {
|
|
actions.setEndpointName('')
|
|
actions.setEndpointDescription('')
|
|
actions.loadEndpoints()
|
|
insightPickerEndpointModalLogic.findMounted()?.actions.closeModal()
|
|
toast.success(<>Endpoint created</>, {
|
|
button: {
|
|
label: 'View',
|
|
action: () => {
|
|
// Close the scene panel (info & actions panel) if endpoint was created from insight
|
|
if (response.derived_from_insight) {
|
|
actions.setScenePanelOpen(false)
|
|
}
|
|
router.actions.push(urls.endpoint(response.name))
|
|
},
|
|
},
|
|
})
|
|
|
|
// Mark endpoint creation task as completed
|
|
globalSetupLogic.findMounted()?.actions.markTaskAsCompleted(SetupTaskId.CreateFirstEndpoint)
|
|
|
|
// Track product intent for sidebar visibility
|
|
const intentContext = response.derived_from_insight
|
|
? ProductIntentContext.ENDPOINT_CREATED_FROM_INSIGHT
|
|
: ProductIntentContext.ENDPOINT_CREATED_FROM_SQL_EDITOR
|
|
actions.addProductIntent({
|
|
product_type: ProductKey.ENDPOINTS,
|
|
intent_context: intentContext,
|
|
})
|
|
},
|
|
createEndpointFailure: ({ queryError }) => {
|
|
if (queryError) {
|
|
toast.error(`Failed to create endpoint: ${queryError}`)
|
|
} else {
|
|
toast.error('Failed to create endpoint')
|
|
}
|
|
},
|
|
updateEndpoint: async ({ name, request, options }) => {
|
|
try {
|
|
const response = await api.endpoint.update(name, request, options?.version)
|
|
actions.updateEndpointSuccess(response, name, options)
|
|
} catch (error: any) {
|
|
console.error('Failed to update endpoint:', error)
|
|
const queryError = error.attr === 'query' ? error.detail : null
|
|
actions.updateEndpointFailure(queryError)
|
|
}
|
|
},
|
|
updateEndpointSuccess: ({ response, endpointName, options }) => {
|
|
actions.setEndpointDescription(null)
|
|
actions.loadEndpoints()
|
|
// Reload versions if we updated a specific version
|
|
if (options?.version) {
|
|
actions.loadVersions(endpointName)
|
|
}
|
|
if (options?.showViewButton) {
|
|
toast.success(<>Endpoint updated</>, {
|
|
button: {
|
|
label: 'View',
|
|
action: () => router.actions.push(urls.endpoint(endpointName)),
|
|
},
|
|
})
|
|
} else {
|
|
toast.success('Endpoint updated')
|
|
actions.loadEndpoint(endpointName)
|
|
}
|
|
|
|
reloadMaterializationStatus(endpointName, options?.version)
|
|
|
|
// Mark activation task as completed when endpoint is activated
|
|
if (response.is_active) {
|
|
globalSetupLogic.findMounted()?.actions.markTaskAsCompleted(SetupTaskId.ConfigureEndpoint)
|
|
}
|
|
},
|
|
updateEndpointFailure: ({ queryError }) => {
|
|
if (queryError) {
|
|
toast.error(`Failed to update endpoint: ${queryError}`)
|
|
} else {
|
|
toast.error('Failed to update endpoint')
|
|
}
|
|
},
|
|
saveTagsInline: async ({ tags }, breakpoint) => {
|
|
const endpoint = values.endpoint
|
|
if (!endpoint) {
|
|
return
|
|
}
|
|
actions.loadEndpointSuccess({ ...endpoint, tags } as EndpointVersionType)
|
|
|
|
await breakpoint(250)
|
|
|
|
try {
|
|
const saved = await api.endpoint.update(endpoint.name, { tags } as Partial<EndpointRequest>)
|
|
breakpoint()
|
|
|
|
actions.loadEndpointSuccess(saved as EndpointVersionType)
|
|
actions.loadEndpoints()
|
|
} catch (error: any) {
|
|
if (error.isBreakpoint) {
|
|
throw error
|
|
}
|
|
actions.loadEndpointSuccess(endpoint)
|
|
toast.error('Failed to save tags')
|
|
}
|
|
},
|
|
deleteEndpoint: async ({ name }) => {
|
|
try {
|
|
await api.endpoint.delete(name)
|
|
actions.deleteEndpointSuccess(name)
|
|
} catch (error) {
|
|
console.error('Failed to delete endpoint:', error)
|
|
actions.deleteEndpointFailure()
|
|
}
|
|
},
|
|
deleteEndpointSuccess: () => {
|
|
toast.success('Endpoint deleted')
|
|
actions.loadEndpoints()
|
|
},
|
|
deleteEndpointFailure: () => {
|
|
toast.error('Failed to delete endpoint')
|
|
},
|
|
confirmToggleActive: ({ endpoint }) => {
|
|
const isActivating = !endpoint.is_active
|
|
Dialog.open({
|
|
title: isActivating ? 'Activate endpoint?' : 'Deactivate endpoint?',
|
|
content: (
|
|
<div className="text-sm text-secondary">
|
|
{isActivating
|
|
? 'Are you sure you want to activate this endpoint? It will be accessible via the API.'
|
|
: 'Are you sure you want to deactivate this endpoint? It will no longer be accessible via the API.'}
|
|
</div>
|
|
),
|
|
primaryButton: {
|
|
children: isActivating ? 'Activate' : 'Deactivate',
|
|
type: 'primary',
|
|
status: isActivating ? undefined : 'danger',
|
|
onClick: () => {
|
|
actions.updateEndpoint(endpoint.name, { is_active: isActivating })
|
|
},
|
|
size: 'small',
|
|
},
|
|
secondaryButton: {
|
|
children: 'Cancel',
|
|
type: 'tertiary',
|
|
size: 'small',
|
|
},
|
|
})
|
|
},
|
|
}
|
|
}),
|
|
permanentlyMount(),
|
|
])
|