Skip to content

Commit 8f74825

Browse files
virajmehtagithub-actions[bot]GabrielBianconi
authored
Added an Add as Demonstration button to the inference TryWithVariant modal (tensorzero#2430)
* wip * added button to try with variant model that adds as demonstration * forgot a file * extracted nested component in VariantResponseModal * added handling for JSON outptu * changed try with variant test to use 4omini * noop * Regenerate ModelInferenceCache fixtures * try to use credentials for try with variant test * Regenerate ModelInferenceCache fixtures * removed duplicate row in model inference cache * reverted model inference cache * Regenerate ModelInferenceCache fixtures * Move button * removed extra OpenAI credential --------- Co-authored-by: TensorZero Bot <github-actions[bot]@users.noreply.github.com> Co-authored-by: Gabriel Bianconi <[email protected]>
1 parent 237201b commit 8f74825

File tree

5 files changed

+275
-108
lines changed

5 files changed

+275
-108
lines changed

.github/workflows/ui-tests-e2e.yml

Lines changed: 55 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,58 @@ on:
1515
required: true
1616

1717
jobs:
18-
1918
ui-tests-no-network:
2019
# We're only using namespace here so that we can download the container artifacts
2120
runs-on: namespace-profile-tensorzero-2x8
2221
steps:
23-
- name: Set DNS
24-
run: echo "127.0.0.1 howdy.tensorzero.com" | sudo tee -a /etc/hosts
25-
- uses: namespacelabs/nscloud-checkout-action@953fed31a6113cc2347ca69c9d823743c65bc84b
26-
- name: Setup Node
27-
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
28-
with:
29-
node-version: "22.9.0"
30-
31-
- name: Download container images
32-
uses: namespace-actions/download-artifact@5c070f7d7ebdc47682b04aa736c76e46ff5f6e1e
33-
with:
34-
pattern: build-*-container
35-
merge-multiple: true
36-
37-
- name: Load `gateway` and `ui` containers
38-
run: |
39-
docker load < gateway-container.tar
40-
docker load < ui-container.tar
41-
42-
# This allows us to use 'no-build' on subsequent steps
43-
- name: Build needed docker images
44-
working-directory: ui
45-
run: |
46-
docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml build fixtures mock-inference-provider
47-
48-
- name: Start docker containers without external network access
49-
working-directory: ui
50-
run: |
51-
# Environment variables shared by the gateway and ui containers
52-
echo "TENSORZERO_CLICKHOUSE_URL=http://chuser:chpassword@clickhouse:8123/tensorzero_ui_fixtures" >> fixtures/.env
53-
echo "TENSORZERO_GATEWAY_URL=http://gateway:3000" >> fixtures/.env
54-
echo "TENSORZERO_GATEWAY_TAG=sha-${{ github.sha }}" >> fixtures/.env
55-
echo "TENSORZERO_UI_TAG=sha-${{ github.sha }}" >> fixtures/.env
56-
echo "TENSORZERO_GATEWAY_CONFIG=/app/config/empty.toml" >> fixtures/.env
57-
echo "TENSORZERO_UI_CONFIG_PATH=/app/config/empty.toml" >> fixtures/.env
58-
59-
export TENSORZERO_SKIP_LARGE_FIXTURES=1
60-
docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml -f ../ci/internal-network.yml up --no-build -d
61-
62-
- name: Print Docker Compose logs
63-
if: always()
64-
working-directory: ui
65-
run: docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml logs -t
66-
67-
- name: Print container health checks
68-
if: always()
69-
working-directory: ui
70-
run: docker inspect --format "{{json .State.Health }}" $(docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml ps -q ui) | jq
22+
- name: Set DNS
23+
run: echo "127.0.0.1 howdy.tensorzero.com" | sudo tee -a /etc/hosts
24+
- uses: namespacelabs/nscloud-checkout-action@953fed31a6113cc2347ca69c9d823743c65bc84b
25+
- name: Setup Node
26+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
27+
with:
28+
node-version: "22.9.0"
29+
30+
- name: Download container images
31+
uses: namespace-actions/download-artifact@5c070f7d7ebdc47682b04aa736c76e46ff5f6e1e
32+
with:
33+
pattern: build-*-container
34+
merge-multiple: true
35+
36+
- name: Load `gateway` and `ui` containers
37+
run: |
38+
docker load < gateway-container.tar
39+
docker load < ui-container.tar
40+
41+
# This allows us to use 'no-build' on subsequent steps
42+
- name: Build needed docker images
43+
working-directory: ui
44+
run: |
45+
docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml build fixtures mock-inference-provider
46+
47+
- name: Start docker containers without external network access
48+
working-directory: ui
49+
run: |
50+
# Environment variables shared by the gateway and ui containers
51+
echo "TENSORZERO_CLICKHOUSE_URL=http://chuser:chpassword@clickhouse:8123/tensorzero_ui_fixtures" >> fixtures/.env
52+
echo "TENSORZERO_GATEWAY_URL=http://gateway:3000" >> fixtures/.env
53+
echo "TENSORZERO_GATEWAY_TAG=sha-${{ github.sha }}" >> fixtures/.env
54+
echo "TENSORZERO_UI_TAG=sha-${{ github.sha }}" >> fixtures/.env
55+
echo "TENSORZERO_GATEWAY_CONFIG=/app/config/empty.toml" >> fixtures/.env
56+
echo "TENSORZERO_UI_CONFIG_PATH=/app/config/empty.toml" >> fixtures/.env
57+
58+
export TENSORZERO_SKIP_LARGE_FIXTURES=1
59+
docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml -f ../ci/internal-network.yml up --no-build -d
60+
61+
- name: Print Docker Compose logs
62+
if: always()
63+
working-directory: ui
64+
run: docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml logs -t
7165

66+
- name: Print container health checks
67+
if: always()
68+
working-directory: ui
69+
run: docker inspect --format "{{json .State.Health }}" $(docker compose -f fixtures/docker-compose.e2e.yml -f fixtures/docker-compose.ui.yml ps -q ui) | jq
7270

7371
ui-tests-gateway-prefix:
7472
# We're only using namespace here so that we can download the container artifacts
@@ -137,6 +135,13 @@ jobs:
137135
continue-on-error: true
138136
run: pnpm ui:test:e2e-base-path
139137

138+
- name: Run UI E2E tests that require credentials
139+
id: e2e_tests_credentials
140+
env:
141+
TENSORZERO_CI: 1
142+
continue-on-error: true
143+
run: pnpm ui:test:e2e --grep "@credentials"
144+
140145
- name: Print Docker Compose logs
141146
if: always()
142147
working-directory: ui
@@ -253,7 +258,7 @@ jobs:
253258
env:
254259
TENSORZERO_CI: 1
255260
continue-on-error: true
256-
run: pnpm ui:test:e2e
261+
run: pnpm ui:test:e2e --grep-invert "@credentials"
257262

258263
- name: Print Docker Compose logs
259264
if: always()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Button } from "../ui/button";
2+
3+
export interface DemonstrationFeedbackButtonProps {
4+
isSubmitting: boolean;
5+
submissionError?: string | null;
6+
}
7+
8+
export function DemonstrationFeedbackButton({
9+
isSubmitting,
10+
submissionError,
11+
}: DemonstrationFeedbackButtonProps) {
12+
return (
13+
<div className="space-y-2">
14+
<Button variant="outline" size="sm" type="submit" disabled={isSubmitting}>
15+
{isSubmitting ? "Adding as Demonstration..." : "Add as Demonstration"}
16+
</Button>
17+
{submissionError && (
18+
<div className="text-sm text-red-600">{submissionError}</div>
19+
)}
20+
</div>
21+
);
22+
}

ui/app/components/inference/VariantResponseModal.tsx

Lines changed: 79 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,73 @@ import type { InferenceResponse } from "~/utils/tensorzero";
1616
import { Card, CardContent } from "~/components/ui/card";
1717
import type { VariantResponseInfo } from "~/routes/api/tensorzero/inference.utils";
1818

19+
interface ResponseColumnProps {
20+
title: string;
21+
response: VariantResponseInfo | null;
22+
errorMessage?: string | null;
23+
children?: React.ReactNode;
24+
item: ParsedInferenceRow | ParsedDatasetRow;
25+
}
26+
27+
function ResponseColumn({
28+
title,
29+
response,
30+
errorMessage,
31+
children,
32+
item,
33+
}: ResponseColumnProps) {
34+
return (
35+
<div className="flex flex-1 flex-col">
36+
<div className="mb-2 flex items-center justify-between">
37+
<h3 className="text-sm font-semibold">{title}</h3>
38+
</div>
39+
{errorMessage ? (
40+
<div className="flex-1">
41+
<Card>
42+
<CardContent className="pt-8">
43+
<div className="text-red-600">
44+
<p className="font-semibold">Error</p>
45+
<p>{errorMessage}</p>
46+
</div>
47+
</CardContent>
48+
</Card>
49+
</div>
50+
) : (
51+
response && (
52+
<>
53+
{response.output && (
54+
<div className="flex-1">
55+
<NewOutput
56+
output={
57+
"output_schema" in item && item.output_schema
58+
? { ...response.output, schema: item.output_schema }
59+
: response.output
60+
}
61+
/>
62+
</div>
63+
)}
64+
65+
<div className="mt-4 grid grid-cols-2 justify-end gap-4">
66+
{response.usage && (
67+
<div>
68+
<h4 className="mb-1 text-xs font-semibold">Usage</h4>
69+
<p className="text-xs">
70+
Input tokens: {response.usage.input_tokens}
71+
</p>
72+
<p className="text-xs">
73+
Output tokens: {response.usage.output_tokens}
74+
</p>
75+
</div>
76+
)}
77+
<div className="flex justify-end">{children}</div>
78+
</div>
79+
</>
80+
)
81+
)}
82+
</div>
83+
);
84+
}
85+
1986
interface VariantResponseModalProps {
2087
isOpen: boolean;
2188
isLoading: boolean;
@@ -30,6 +97,7 @@ interface VariantResponseModalProps {
3097
error?: string | null;
3198
variantResponse: VariantResponseInfo | null;
3299
rawResponse: InferenceResponse | null;
100+
children?: React.ReactNode;
33101
}
34102

35103
export function VariantResponseModal({
@@ -43,6 +111,7 @@ export function VariantResponseModal({
43111
error,
44112
variantResponse,
45113
rawResponse,
114+
children,
46115
}: VariantResponseModalProps) {
47116
const [showRawResponse, setShowRawResponse] = useState(false);
48117

@@ -63,60 +132,6 @@ export function VariantResponseModal({
63132
setShowRawResponse(false);
64133
}, [isOpen]);
65134

66-
const ResponseColumn = ({
67-
title,
68-
response,
69-
errorMessage,
70-
}: {
71-
title: string;
72-
response: VariantResponseInfo | null;
73-
errorMessage?: string | null;
74-
}) => (
75-
<div className="flex flex-1 flex-col">
76-
<h3 className="mb-2 text-sm font-semibold">{title}</h3>
77-
{errorMessage ? (
78-
<div className="flex-1">
79-
<Card>
80-
<CardContent className="pt-8">
81-
<div className="text-red-600">
82-
<p className="font-semibold">Error</p>
83-
<p>{errorMessage}</p>
84-
</div>
85-
</CardContent>
86-
</Card>
87-
</div>
88-
) : (
89-
response && (
90-
<>
91-
{response.output && (
92-
<div className="flex-1">
93-
<NewOutput
94-
output={
95-
"output_schema" in item && item.output_schema
96-
? { ...response.output, schema: item.output_schema }
97-
: response.output
98-
}
99-
/>
100-
</div>
101-
)}
102-
103-
{response.usage && (
104-
<div className="mt-4">
105-
<h4 className="mb-1 text-xs font-semibold">Usage</h4>
106-
<p className="text-xs">
107-
Input tokens: {response.usage.input_tokens}
108-
</p>
109-
<p className="text-xs">
110-
Output tokens: {response.usage.output_tokens}
111-
</p>
112-
</div>
113-
)}
114-
</>
115-
)
116-
)}
117-
</div>
118-
);
119-
120135
// Create a dynamic title based on the source
121136
const getTitle = () => {
122137
if (source === "inference") {
@@ -165,13 +180,21 @@ export function VariantResponseModal({
165180
) : (
166181
<>
167182
<div className="flex flex-col gap-4 md:grid md:min-h-[300px] md:grid-cols-2">
168-
<ResponseColumn title="Original" response={baselineResponse} />
183+
<ResponseColumn
184+
title="Original"
185+
response={baselineResponse}
186+
item={item}
187+
/>
169188
<ResponseColumn
170189
title="New"
171190
response={variantResponse}
172191
errorMessage={error}
173-
/>
192+
item={item}
193+
>
194+
{children}
195+
</ResponseColumn>
174196
</div>
197+
175198
<Separator className="my-4" />
176199
<div>
177200
<Button

0 commit comments

Comments
 (0)