Add a New Curve Format
SkillDev toolsAdd support for a new curve data format in the Curves package converter system
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Add a New Curve Format skill
What this skill tells your AI
The instructions your AI receives, as published by datagrok-ai/public in .claude/skills/add-curve-format/SKILL.md and read by ahel’s review.
Help the user add support for a new curve data format to Datagrok. The Curves package (packages/Curves) uses a converter architecture where any package can provide support for a new curve notation. When the Curves package initializes, it discovers all converter functions tagged with meta.role: curveConverter and meta.curveFormat: '<name>' across all loaded packages and registers them automatically.
Usage
/add-curve-format [format-name]
Background
Native Format
The native format is IFitChartData JSON (defined in @datagrok-libraries/statistics/src/fit/fit-curve.ts):
interface IFitChartData {
chartOptions?: IFitChartOptions; // axes bounds, log scale, title, axis names
seriesOptions?: IFitSeriesOptions; // default series options
series?: IFitSeries[]; // array of data series with points
}
Each IFitSeries has points: IFitPoint[] where each point has x, y, and optional outlier, plus fit configuration like fitFunction, parameters, showFitLine, etc.
How the Converter System Works
- Detector (in your package's
detectors.js) identifies a column's format and sets:col.semType = 'fit'col.setTag('.%curve-format', '<format-name>')— a friendly format name (e.g.'my-format')
- Converter DG function (in your package, tagged
meta.role: curveConverterandmeta.curveFormat: '<format-name>') returns a sync converter function:() => (value: string) => string - Auto-discovery: The Curves package calls
DG.Func.find({meta: {role: 'curveConverter'}})at init time, reads each function'smeta.curveFormatvalue, and registers the converter under that format name - Caching: Converted values are cached in a value-based LRU cache keyed by
formatName||cellValue - All rendering, property panels, and statistics use
parseCellValue()which handles conversion transparently
Important: Your package does NOT depend on Curves
Your package only needs @datagrok-libraries/statistics (for types like IFitChartData and FitConstants) and datagrok-api. The Curves package discovers your converter automatically at runtime.
Instructions
Step 1: Create the Converter Function
Create a converter file in your package, e.g. src/<format-name>-converter.ts:
import {
IFitChartData,
IFitPoint,
IFitSeries,
} from '@datagrok-libraries/statistics/src/fit/fit-curve';
/** Converts <format-name> format to native IFitChartData JSON string. */
export function convert<FormatName>ToJson(value: string): string {
// 1. Parse the input format (JSON.parse, DOMParser, etc.)
// 2. Extract points, series options, chart options
// 3. Build IFitChartData object
// 4. Return JSON.stringify(chartData)
const parsed = JSON.parse(value); // or XML parse, etc.
const points: IFitPoint[] = []; // Map input data to {x, y, outlier?}
// ... populate points from parsed data
const series: IFitSeries = {
points: points,
fitFunction: 'sigmoid', // or '4pl-dose-response', 'linear', etc.
showFitLine: true,
showPoints: 'points',
clickToToggle: true,
};
// If the format includes pre-fit parameters, set them:
// series.parameters = [max, slope, IC50, min];
const chartData: IFitChartData = {
chartOptions: {
xAxisName: 'Concentration',
yAxisName: 'Response',
logX: true, // typical for dose-response
},
series: [series],
};
return JSON.stringify(chartData);
}
Key requirements for the converter function:
- Signature:
(value: string) => string— takes raw cell value, returns native JSON string - Pure function: no side effects, no async operations (rendering is synchronous)
- Handle edge cases: empty/null input, malformed data
- Map all relevant fields: points (x, y), outliers, fit parameters, axis labels, units
Step 2: Register the Converter as a DG Function
In your package's src/package.ts, expose a DG function tagged with meta.role: curveConverter and meta.curveFormat: '<format-name>'. This function returns the sync converter function (it does not perform the conversion itself):
import {convert<FormatName>ToJson} from './<format-name>-converter';
export class PackageFunctions {
@grok.decorators.func({
description: 'Returns <format-name> curve converter function',
meta: {role: 'curveConverter', curveFormat: '<format-name>'}
})
static convert<FormatName>ToJsonFunc(): (value: string) => string {
return convert<FormatName>ToJson;
}
}
The meta.curveFormat value must match exactly the format name set in the detector tag.
Step 3: Add a Detector
Add a semantic type detector in your package's detectors.js:
const FIT_SEM_TYPE = 'fit';
const TAG_CURVE_FORMAT = '.%curve-format';
class <YourPackage>PackageDetectors extends DG.Package {
//meta.role: semTypeDetector
//input: column col
//output: string semType
detect<FormatName>(col) {
if (DG.Detector.sampleCategories(col, (s) => {
// Return true if the string looks like this format
// Use fast checks: string includes, startsWith, etc.
// For JSON: try { const o = JSON.parse(s); return hasExpectedFields; } catch { return false; }
return false;
}, 1)) {
col.setTag(TAG_CURVE_FORMAT, '<format-name>');
col.semType = FIT_SEM_TYPE;
return col.semType;
}
return null;
}
}
Detection tips:
- Use
DG.Detector.sampleCategories(col, predicate, minCount)to check sample values - Keep detection fast — avoid expensive parsing in the detector
- Check for format-specific markers (XML tags, JSON field names, magic strings)
- The format name in
setTagmust matchmeta.curveFormaton the converter function exactly
Step 4: Add Tests
Add tests to your package's test files:
import {convert<FormatName>ToJson} from '../<format-name>-converter';
import {IFitChartData} from '@datagrok-libraries/statistics/src/fit/fit-curve';
import {FitConstants} from '@datagrok-libraries/statistics/src/fit/const';
// Sample data in the new format
const SAMPLE_DATA = '...';
category('curve converter', () => {
test('basicConversion', async () => {
const result = convert<FormatName>ToJson(SAMPLE_DATA);
const chartData: IFitChartData = JSON.parse(result);
expect(chartData.series !== undefined && chartData.series.length > 0, true);
expect(chartData.series![0].points.length > 0, true);
// Verify specific point values, chart options, etc.
});
test('detection', async () => {
const df = DG.DataFrame.fromColumns([DG.Column.fromStrings('col', [SAMPLE_DATA])]);
const col = df.columns.byName('col');
await grok.data.detectSemanticTypes(df);
expect(col.semType, FitConstants.FIT_SEM_TYPE);
expect(col.getTag(FitConstants.TAG_CURVE_FORMAT), '<format-name>');
});
});
Step 5: Build and Test
cd <YourPackage>
npm run build
grok test --host <host alias in config>
Once both your package and the Curves package are published to the same server, the Curves package will automatically discover and use your converter.
Reference: Existing Converters in the Curves Package
For working examples, see these files in packages/Curves/:
| Format | Format Name | Converter | Detector |
|---|---|---|---|
XML 3DX (<chart>...</chart>) | 3dx | src/fit/converters/xml-converter.ts | detectXMLCurveChart |
Compact dose-response JSON ({p, poi, mxr, ...}) | compact-dr | src/fit/converters/compact-dr-converter.ts | detectCompactDoseResponse |
| PZFX (GraphPad Prism XY) | pzfx | src/fit/converters/pzfx-converter.ts | detectPzfxCurveChart |
Note: the Curves package registers its own converters directly (hardcoded) to avoid circular dependencies. External packages rely on auto-discovery via meta.curveFormat.
Reference: Key Types
From @datagrok-libraries/statistics/src/fit/fit-curve.ts:
interface IFitPoint {
x: number;
y: number;
outlier?: boolean;
color?: string;
marker?: FitMarkerType;
size?: number;
stdev?: number;
}
interface IFitSeries {
points: IFitPoint[];
name?: string;
fitFunction?: string; // 'sigmoid', '4pl-dose-response', 'linear', etc.
parameters?: number[]; // [max, slope, IC50, min] for sigmoid/4PL
showFitLine?: boolean;
showPoints?: string; // 'points' to show
clickToToggle?: boolean; // allow outlier toggling
pointColor?: string;
fitLineColor?: string;
droplines?: string[]; // e.g. ['IC50']
// ... more options available
}
interface IFitChartOptions {
minX?: number; minY?: number;
maxX?: number; maxY?: number;
xAxisName?: string;
yAxisName?: string;
logX?: boolean;
logY?: boolean;
title?: string;
// ... more options available
}
From @datagrok-libraries/statistics/src/fit/const.ts:
FitConstants.TAG_CURVE_FORMAT='.%curve-format'— the column tag to setFitConstants.FIT_SEM_TYPE='fit'— the semantic type to assign
Available fit functions: 'sigmoid', '4pl-dose-response', '4pl-regression', 'linear', 'log-linear', 'exponential', or custom via IFitFunctionDescription.
Signals
- GitHub stars
- 72
- Forks
- 32
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
add-curve-format- Source
- github.com/datagrok-ai/public