remove classify as a user facing tool

This commit is contained in:
EthanHealy01
2026-06-29 22:10:09 +01:00
parent fed7ad300f
commit a92722ff13
11 changed files with 43 additions and 48 deletions
@@ -1,7 +1,5 @@
package stirling.software.proprietary.controller.api;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
@@ -19,6 +17,8 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
@@ -152,8 +152,8 @@ public class ClassifyTagController {
/**
* Override point for a future per-org / DB-configured taxonomy: resolve the caller's taxonomy
* here and return it (engine shape) to classify against; {@code null} falls back to the engine's
* generated default. Always null today.
* here and return it (engine shape) to classify against; {@code null} falls back to the
* engine's generated default. Always null today.
*/
private JsonNode resolveTaxonomyOverride() {
return null;
@@ -1,4 +1,5 @@
{
"_generated": "AUTO-GENERATED from frontend/editor/src/proprietary/data/classificationTaxonomy.ts by editor/scripts/generate-taxonomy.mts — do NOT edit by hand; run `task frontend:taxonomy`.",
"categories": [
{
"id": "invoice",
@@ -1,5 +1,6 @@
from __future__ import annotations
import json
import logging
from pathlib import Path
@@ -36,7 +37,12 @@ WINDOW_PAGES = 2
# `task frontend:taxonomy` — edit that file, not this JSON. Validated into the
# typed contract on import, so a malformed entry fails fast.
_DEFAULT_TAXONOMY_PATH = Path(__file__).with_name("default_taxonomy.generated.json")
DEFAULT_TAXONOMY = ClassificationTaxonomy.model_validate_json(_DEFAULT_TAXONOMY_PATH.read_text(encoding="utf-8"))
# The file carries an underscore-prefixed "_generated" notice (JSON has no
# comments); drop meta keys before validating against the strict contract.
_raw_taxonomy = json.loads(_DEFAULT_TAXONOMY_PATH.read_text(encoding="utf-8"))
DEFAULT_TAXONOMY = ClassificationTaxonomy.model_validate(
{key: value for key, value in _raw_taxonomy.items() if not key.startswith("_")}
)
_SYSTEM_PROMPT = (
-1
View File
@@ -63,7 +63,6 @@ class WorkflowOutcome(StrEnum):
UNSUPPORTED_CAPABILITY = "unsupported_capability"
GENERATE_FILE = "generate_file"
CONVERT_MARKDOWN = "convert_markdown"
CLASSIFICATION = "classification"
class ArtifactKind(StrEnum):
@@ -1,12 +1,9 @@
from __future__ import annotations
from typing import Literal
from pydantic import Field
from stirling.models import ApiModel
from .common import WorkflowOutcome
from .documents import PageText
@@ -55,10 +52,11 @@ class DocumentClassificationResponse(ApiModel):
``category`` and ``doc_type`` are ids drawn from the taxonomy, or the
sentinel ``"unknown"`` when the model's answer fell outside it. ``tags`` are
the subset of the model's tags that exist in the taxonomy.
the subset of the model's tags that exist in the taxonomy. This is a plain
answer from a dedicated endpoint — it carries no ``outcome`` discriminator
(it isn't one of the orchestrator's WorkflowOutcome-routed union responses).
"""
outcome: Literal[WorkflowOutcome.CLASSIFICATION] = WorkflowOutcome.CLASSIFICATION
category: str
doc_type: str
type_confidence: float = Field(ge=0.0, le=1.0)
@@ -44,7 +44,6 @@ def test_classify_returns_camel_cased_result(classification_client: TestClient)
)
assert response.status_code == 200
body = response.json()
assert body["outcome"] == "classification"
assert body["category"] == "contract"
assert body["docType"] == "nda"
assert body["typeConfidence"] == 0.96
@@ -4298,10 +4298,6 @@ desc = "Compares and shows the differences between 2 PDF Documents"
tags = "difference,compare,diff,compare PDFs,compare documents,find differences,show differences,changes,what changed,track changes,revisions,version compare,side by side,contrast,delta"
title = "Compare"
[home.classify]
desc = "Identify a document's type and tag its metadata."
title = "Classify"
[home.compress]
desc = "Compress PDFs to reduce their file size."
tags = "shrink,reduce,optimize,compress,smaller,downsize,file size,reduce size,minimize,make smaller,decrease size,optimize size"
@@ -34,7 +34,15 @@ const outPath = resolve(
"engine/src/stirling/agents/default_taxonomy.generated.json",
);
const json = JSON.stringify(DEFAULT_CLASSIFICATION_TAXONOMY, null, 2) + "\n";
const NOTICE =
"AUTO-GENERATED from frontend/editor/src/proprietary/data/classificationTaxonomy.ts " +
"by editor/scripts/generate-taxonomy.mts — do NOT edit by hand; run `task frontend:taxonomy`.";
const json =
JSON.stringify(
{ _generated: NOTICE, ...DEFAULT_CLASSIFICATION_TAXONOMY },
null,
2,
) + "\n";
if (process.argv.includes("--check")) {
const current = existsSync(outPath) ? readFileSync(outPath, "utf8") : "";
@@ -489,6 +489,7 @@ export async function runPolicyOnFile(
backendId: string,
fileId: FileId,
fileName: string,
replaceRunId?: string,
): Promise<void> {
// A freshly-uploaded file's bytes are written to IndexedDB asynchronously, so
// its stub can appear in the file list a beat before getStirlingFile resolves
@@ -516,6 +517,7 @@ export async function runPolicyOnFile(
}
try {
const runId = await runStoredPolicy(backendId, [file]);
if (replaceRunId) removeRun(replaceRunId);
// recordRunStart marks this (policy, file) dispatched as it records the run.
recordRunStart({
runId,
@@ -1,27 +0,0 @@
import { ToolType } from "@app/hooks/tools/shared/useToolOperation";
/** Classify-and-tag takes no user parameters — it only needs the file. */
export type ClassifyParameters = Record<string, never>;
export const defaultParameters: ClassifyParameters = {};
// Static function shared by the registry/automation executor. The backend reads
// only the file: it classifies via the AI engine and writes the result into the
// StirlingPDFClassification metadata field.
export const buildClassifyFormData = (
_parameters: ClassifyParameters,
file: File,
): FormData => {
const formData = new FormData();
formData.append("fileInput", file);
return formData;
};
export const classifyOperationConfig = {
toolType: ToolType.singleFile,
buildFormData: buildClassifyFormData,
operationType: "classify",
endpoint: "/api/v1/ai/tools/classify-and-tag",
multiFileEndpoint: false,
defaultParameters,
} as const;
@@ -95,6 +95,16 @@ export interface PolicyRunView {
createdAt: number;
}
/**
* Operations that run as policy pipeline steps but are NOT user-facing tools, so
* they have no tool-registry entry and never appear in the tool picker. Maps the
* operation id straight to its backend endpoint.
*/
const POLICY_OPERATION_ENDPOINTS: Record<string, string> = {
// Document classification — dispatched only by the Classification policy.
classify: "/api/v1/ai/tools/classify-and-tag",
};
/** Resolve a frontend operation id to its backend tool endpoint path. */
function resolveEndpoint(
operation: string,
@@ -103,10 +113,13 @@ function resolveEndpoint(
): string | null {
const config = toolRegistry[operation as keyof ToolRegistry]?.operationConfig;
const endpoint = config?.endpoint;
if (!endpoint) return null;
const resolved =
typeof endpoint === "function" ? endpoint(parameters) : endpoint;
return resolved ?? null;
if (endpoint) {
const resolved =
typeof endpoint === "function" ? endpoint(parameters) : endpoint;
if (resolved) return resolved;
}
// Policy-only operations have no registry entry; resolve them directly.
return POLICY_OPERATION_ENDPOINTS[operation] ?? null;
}
/**