(llm-first): context budget, validation, and eval harness, orchestration general-prompt
This commit is contained in:
@@ -1,146 +1,174 @@
|
||||
import { DataProvider, fetchUtils } from 'react-admin';
|
||||
import { getValidAccessToken } from './auth/keycloak';
|
||||
import { env } from './config/env';
|
||||
import type { DataProvider } from "react-admin";
|
||||
import { env } from "./config/env";
|
||||
import { getAccessToken } from "./auth/keycloak";
|
||||
|
||||
const apiUrl = env.apiUrl;
|
||||
async function fetchJson(
|
||||
url: string,
|
||||
options: RequestInit = {},
|
||||
): Promise<{ json: any; headers: Headers; status: number }> {
|
||||
const headers = new Headers(
|
||||
options.headers ?? { Accept: "application/json" },
|
||||
);
|
||||
const token = await getAccessToken();
|
||||
if (token) {
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
if (!headers.has("Content-Type") && options.body) {
|
||||
headers.set("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
const httpClient = async (url: string, options: fetchUtils.Options = {}) => {
|
||||
const token = await getValidAccessToken();
|
||||
const headers = new Headers(options.headers ?? { Accept: 'application/json' });
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
return fetchUtils.fetchJson(url, {
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
};
|
||||
|
||||
function buildQueryString(query: Record<string, unknown>) {
|
||||
const search = new URLSearchParams();
|
||||
Object.entries(query).forEach(([key, val]) => {
|
||||
if (val === undefined || val === null || val === '') return;
|
||||
if (Array.isArray(val)) {
|
||||
val.forEach((v) => {
|
||||
if (v === undefined || v === null || v === '') return;
|
||||
search.append(key, String(v));
|
||||
});
|
||||
return;
|
||||
const response = await fetch(url, { ...options, headers });
|
||||
if (!response.ok) {
|
||||
const error = new Error(
|
||||
"Request failed with status " + response.status,
|
||||
) as Error & { status?: number; body?: unknown };
|
||||
error.status = response.status;
|
||||
try {
|
||||
error.body = await response.json();
|
||||
} catch {
|
||||
error.body = null;
|
||||
}
|
||||
search.set(key, String(val));
|
||||
});
|
||||
return search.toString();
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return { json: null, headers: response.headers, status: response.status };
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
return { json, headers: response.headers, status: response.status };
|
||||
}
|
||||
|
||||
const dataProvider: DataProvider = {
|
||||
function appendSearchParam(
|
||||
searchParams: URLSearchParams,
|
||||
key: string,
|
||||
value: unknown,
|
||||
): void {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((entry) => appendSearchParam(searchParams, key, entry));
|
||||
return;
|
||||
}
|
||||
if (value === undefined || value === null || value === "") {
|
||||
return;
|
||||
}
|
||||
searchParams.append(key, String(value));
|
||||
}
|
||||
|
||||
function buildListUrl(resource: string, params: any): string {
|
||||
const resourcePath = resource === "equipment" ? "equipments" : resource;
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set(
|
||||
"_start",
|
||||
String((params.pagination.page - 1) * params.pagination.perPage),
|
||||
);
|
||||
searchParams.set(
|
||||
"_end",
|
||||
String(params.pagination.page * params.pagination.perPage),
|
||||
);
|
||||
searchParams.set("_sort", params.sort.field);
|
||||
searchParams.set("_order", params.sort.order);
|
||||
Object.entries(params.filter ?? {}).forEach(([key, value]) => {
|
||||
appendSearchParam(searchParams, key, value);
|
||||
});
|
||||
const queryString = searchParams.toString();
|
||||
return (
|
||||
env.apiUrl + "/" + resourcePath + (queryString ? "?" + queryString : "")
|
||||
);
|
||||
}
|
||||
|
||||
export const dataProvider: DataProvider = {
|
||||
getList: async (resource, params) => {
|
||||
const { page, perPage } = params.pagination!;
|
||||
const { field, order } = params.sort!;
|
||||
const start = (page - 1) * perPage;
|
||||
const end = page * perPage;
|
||||
|
||||
const query: Record<string, unknown> = {
|
||||
_start: start,
|
||||
_end: end,
|
||||
_sort: field,
|
||||
_order: order,
|
||||
...(params.filter ?? {}),
|
||||
};
|
||||
|
||||
const queryString = buildQueryString(query);
|
||||
const url = `${apiUrl}/${resource}?${queryString}`;
|
||||
const { json, headers } = await httpClient(url);
|
||||
|
||||
const contentRange = headers.get('Content-Range');
|
||||
if (resource === "price-list") {
|
||||
const { json } = await fetchJson(env.apiUrl + "/price-list");
|
||||
return { data: [json], total: 1 };
|
||||
}
|
||||
const { json, headers } = await fetchJson(buildListUrl(resource, params));
|
||||
const contentRange = headers.get("Content-Range");
|
||||
const total = contentRange
|
||||
? parseInt(contentRange.split('/').pop() || '0', 10)
|
||||
: json.length;
|
||||
|
||||
return { data: json, total };
|
||||
? Number(
|
||||
contentRange.split("/").pop() ??
|
||||
(Array.isArray(json) ? json.length : 0),
|
||||
)
|
||||
: Array.isArray(json)
|
||||
? json.length
|
||||
: 0;
|
||||
return { data: Array.isArray(json) ? json : [], total };
|
||||
},
|
||||
|
||||
getOne: async (resource, params) => {
|
||||
const { json } = await httpClient(`${apiUrl}/${resource}/${params.id}`);
|
||||
const resourcePath = resource === "equipment" ? "equipments" : resource;
|
||||
const url =
|
||||
resource === "price-list"
|
||||
? env.apiUrl + "/price-list"
|
||||
: env.apiUrl + "/" + resourcePath + "/" + params.id;
|
||||
const { json } = await fetchJson(url);
|
||||
return { data: json };
|
||||
},
|
||||
|
||||
getMany: async (resource, params) => {
|
||||
const query = params.ids.map((id) => `id=${id}`).join('&');
|
||||
const { json } = await httpClient(`${apiUrl}/${resource}?${query}`);
|
||||
return { data: json };
|
||||
if (resource === "price-list") {
|
||||
const { json } = await fetchJson(env.apiUrl + "/price-list");
|
||||
return { data: params.ids.includes("price-list") ? [json] : [] };
|
||||
}
|
||||
const records = await Promise.all(
|
||||
params.ids.map((id) =>
|
||||
dataProvider.getOne(resource, { id, meta: params.meta } as any),
|
||||
),
|
||||
);
|
||||
return { data: records.map((result) => result.data) };
|
||||
},
|
||||
|
||||
getManyReference: async (resource, params) => {
|
||||
const { page, perPage } = params.pagination!;
|
||||
const { field, order } = params.sort!;
|
||||
const start = (page - 1) * perPage;
|
||||
const end = page * perPage;
|
||||
|
||||
const query: Record<string, unknown> = {
|
||||
_start: start,
|
||||
_end: end,
|
||||
_sort: field,
|
||||
_order: order,
|
||||
[params.target]: params.id,
|
||||
...(params.filter ?? {}),
|
||||
};
|
||||
|
||||
const queryString = buildQueryString(query);
|
||||
const url = `${apiUrl}/${resource}?${queryString}`;
|
||||
const { json, headers } = await httpClient(url);
|
||||
|
||||
const contentRange = headers.get('Content-Range');
|
||||
const total = contentRange
|
||||
? parseInt(contentRange.split('/').pop() || '0', 10)
|
||||
: json.length;
|
||||
|
||||
return { data: json, total };
|
||||
},
|
||||
|
||||
getManyReference: async (resource, params) =>
|
||||
dataProvider.getList(resource, {
|
||||
pagination: params.pagination,
|
||||
sort: params.sort,
|
||||
filter: { ...(params.filter ?? {}), [params.target]: params.id },
|
||||
meta: params.meta,
|
||||
} as any),
|
||||
create: async (resource, params) => {
|
||||
const { json } = await httpClient(`${apiUrl}/${resource}`, {
|
||||
method: 'POST',
|
||||
const resourcePath = resource === "equipment" ? "equipments" : resource;
|
||||
const { json } = await fetchJson(env.apiUrl + "/" + resourcePath, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params.data),
|
||||
});
|
||||
return { data: json };
|
||||
},
|
||||
|
||||
update: async (resource, params) => {
|
||||
const { json } = await httpClient(`${apiUrl}/${resource}/${params.id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(params.data),
|
||||
});
|
||||
const resourcePath = resource === "equipment" ? "equipments" : resource;
|
||||
const { json } = await fetchJson(
|
||||
env.apiUrl + "/" + resourcePath + "/" + params.id,
|
||||
{ method: "PATCH", body: JSON.stringify(params.data) },
|
||||
);
|
||||
return { data: json };
|
||||
},
|
||||
|
||||
updateMany: async (resource, params) => {
|
||||
const responses = await Promise.all(
|
||||
const results = await Promise.all(
|
||||
params.ids.map((id) =>
|
||||
httpClient(`${apiUrl}/${resource}/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(params.data),
|
||||
})
|
||||
)
|
||||
dataProvider.update(resource, {
|
||||
id,
|
||||
data: params.data,
|
||||
previousData: {},
|
||||
meta: params.meta,
|
||||
} as any),
|
||||
),
|
||||
);
|
||||
return { data: responses.map(({ json }) => json.id) };
|
||||
return { data: results.map((result) => result.data.id) };
|
||||
},
|
||||
|
||||
delete: async (resource, params) => {
|
||||
const { json } = await httpClient(`${apiUrl}/${resource}/${params.id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
return { data: json };
|
||||
},
|
||||
|
||||
deleteMany: async (resource, params) => {
|
||||
const responses = await Promise.all(
|
||||
params.ids.map((id) =>
|
||||
httpClient(`${apiUrl}/${resource}/${id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
)
|
||||
const resourcePath = resource === "equipment" ? "equipments" : resource;
|
||||
const { json } = await fetchJson(
|
||||
env.apiUrl + "/" + resourcePath + "/" + params.id,
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
return { data: responses.map(({ json }) => json.id) };
|
||||
return { data: json ?? { id: params.id } };
|
||||
},
|
||||
deleteMany: async (resource, params) => {
|
||||
const results = await Promise.all(
|
||||
params.ids.map((id) =>
|
||||
dataProvider.delete(resource, {
|
||||
id,
|
||||
previousData: {},
|
||||
meta: params.meta,
|
||||
} as any),
|
||||
),
|
||||
);
|
||||
return { data: results.map((result) => result.data.id) };
|
||||
},
|
||||
};
|
||||
|
||||
export default dataProvider;
|
||||
|
||||
Reference in New Issue
Block a user