Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/components/map-projects/MapProject.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,8 @@ const MapProject = () => {
// event would arrive where the options object is expected.
const onManualSave = () => onSave({source: 'manual'})

// Work on a row other than the one open in the side panel (a run's, a grid
// button's) must pass its index (OpenConceptLab/ocl_issues#2887).
const log = (data, index) => {
let idx = index === undefined ? rowIndex : index
const nextLogs = {...logsRef.current, [idx]: [{created_at: moment().toDate(), user: user.username || user.id, ...data}, ...(logsRef.current[idx] || [])]}
Expand Down Expand Up @@ -5721,7 +5723,9 @@ const MapProject = () => {
// Returns the row's AI outcome as a stage (1 analysed, -2 failed, -3 not
// run), or null when the row was skipped or the run stopped. A run's call
// that ends after a newer run started leaves that run's stages and failure
// count alone (ocl_issues#2872).
// count alone (ocl_issues#2872), but still logs on its own row, like every
// AI entry here: the row's history records each call made for it
// (OpenConceptLab/ocl_issues#2887).
const runTicket = autoMatchRunTicketRef.current
const isSuperseded = () => isBulk && runTicket !== autoMatchRunTicketRef.current
const markRecommend = stage => {
Expand Down Expand Up @@ -5842,14 +5846,14 @@ const MapProject = () => {
let timestamp = moment().toDate()
if(handlePreviewLimitError(response, isSuperseded() ? null : __index, 'recommend', {isRun: isBulk, stopPhase: 'ai'})) {
markRecommend(-3)
log({created_at: timestamp, action: 'AIRecommendationLimitReached', extras: {model: requestedModel, prompt_template: promptTemplateRef, prompt_template_uri: promptTemplateRef?.uri}})
log({created_at: timestamp, action: 'AIRecommendationLimitReached', extras: {model: requestedModel, prompt_template: promptTemplateRef, prompt_template_uri: promptTemplateRef?.uri}}, __index)
return -3
}
if(response?.detail) {
markRecommend(-2)
if(!isSuperseded())
aiFailuresInARowRef.current += 1
log({created_at: timestamp, action: 'AIRecommendation', description: response.detail, extras: {error: response.detail, model: requestedModel, prompt_template: promptTemplateRef, prompt_template_uri: promptTemplateRef?.uri}})
log({created_at: timestamp, action: 'AIRecommendation', description: response.detail, extras: {error: response.detail, model: requestedModel, prompt_template: promptTemplateRef, prompt_template_uri: promptTemplateRef?.uri}}, __index)
setAlert({message: response.detail, severity: 'error'})
return -2
}
Expand Down Expand Up @@ -5882,7 +5886,7 @@ const MapProject = () => {
} catch (err) {
if(handlePreviewLimitError(err, isSuperseded() ? null : __index, 'recommend', {isRun: isBulk, stopPhase: 'ai'})) {
markRecommend(-3)
log({created_at: moment().toDate(), action: 'AIRecommendationLimitReached', extras: {model: requestedModel, prompt_template: promptTemplateRef, prompt_template_uri: promptTemplateRef?.uri}})
log({created_at: moment().toDate(), action: 'AIRecommendationLimitReached', extras: {model: requestedModel, prompt_template: promptTemplateRef, prompt_template_uri: promptTemplateRef?.uri}}, __index)
return -3
}
markRecommend(-2)
Expand Down
32 changes: 32 additions & 0 deletions src/components/map-projects/__tests__/aiRecommendationLogs.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Every entry fetchRecommendation logs goes on the row it analysed, not the
* row open in the side panel (OpenConceptLab/ocl_issues#2887).
*
* fetchRecommendation is a closure inside MapProject, which these tests can't
* mount (they run without a JSX transform), so this reads its source: each
* log() call in it must pass __index, the row it was called for.
*/

import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import babel from '@babel/core'

test('fetchRecommendation: every AI entry it logs goes on the row being analysed, superseded or not', () => {
const source = readFileSync(new URL('../MapProject.jsx', import.meta.url), 'utf8')
const calls = []
babel.traverse(babel.parseSync(source, {babelrc: false, configFile: false, parserOpts: {plugins: ['jsx']}}), {
VariableDeclarator(path) {
if(path.node.id.name === 'fetchRecommendation')
path.traverse({
CallExpression({node}) {
if(node.callee.name === 'log')
calls.push({line: node.loc.start.line, row: node.arguments[1]?.name})
}
})
}
})

assert.ok(calls.length > 0, 'no log() calls found in fetchRecommendation')
assert.deepEqual(calls.filter(call => call.row !== '__index'), [])
})
Loading