Skip to content

Commit 46012bb

Browse files
chore(lint): fix revive, unconvert, and testifylint in image generation and tracing
- Ignore unused parameters in tests and translator (revive)\n- Remove unnecessary string conversions now that model types are strings (unconvert)\n- Use require.Empty for empty string check (testifylint)\n- Remove unused test variable in span tests (unused)\n- Keep extractUsageFromBufferEvent with lint ignore for future parity
1 parent 9e45c62 commit 46012bb

18 files changed

+98
-57
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,5 @@ inference-extension-conformance-test-report.yaml
4949

5050
.goose
5151
tests/e2e-inference-extension/logs/
52+
53+
/aigw

cmd/aigw/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ Here are values we use for Ollama:
5151
docker compose run --rm embeddings
5252
```
5353

54+
- Image generation:
55+
- Using service:
56+
```bash
57+
docker compose run --rm image-generation
58+
```
59+
- Using curl (save to file):
60+
```bash
61+
curl -s \
62+
-X POST http://localhost:1975/v1/images/generations \
63+
-H "Authorization: Bearer unused" \
64+
-H "Content-Type: application/json" \
65+
-d '{"model":"gpt-image-1","prompt":"A watercolor painting of a red fox in a birch forest","size":"512x512"}' \
66+
| jq -r '.data[0].b64_json' | base64 -d > image.png
67+
```
68+
5469
4. **Create embeddings**:
5570

5671
The `create-embeddings` service uses `curl` to send an embeddings request
@@ -144,6 +159,8 @@ This configures the OTLP endpoint to otel-tui on port 4318.
144159
```bash
145160
COMPOSE_PROFILES=<profile> docker compose -f docker-compose-otel.yaml run --build --rm chat-completion
146161
COMPOSE_PROFILES=<profile> docker compose -f docker-compose-otel.yaml run --build --rm create-embeddings
162+
# Image generation
163+
COMPOSE_PROFILES=<profile> docker compose -f docker-compose-otel.yaml run --build --rm image-generation
147164
```
148165

149166
3. **Check telemetry output**:

cmd/aigw/docker-compose.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,23 @@ services:
9999
-d "{\"model\":\"$$EMBEDDINGS_MODEL\",\"input\":\"How do I reset my password?\"}"
100100
extra_hosts: # localhost:host-gateway trick doesn't work with aigw
101101
- "host.docker.internal:host-gateway"
102+
103+
# image-generation is a simple curl-based test client for sending image
104+
# generation requests to aigw.
105+
image-generation:
106+
image: golang:1.25
107+
container_name: image-generation
108+
profiles: ["test"]
109+
env_file:
110+
- ../../.env.ollama
111+
command:
112+
- sh
113+
- -c
114+
- |
115+
curl -s -w %{http_code} \
116+
-X POST http://aigw:1975/v1/images/generations \
117+
-H "Authorization: Bearer unused" \
118+
-H "Content-Type: application/json" \
119+
-d "{\"model\":\"$$CHAT_MODEL\",\"prompt\":\"A watercolor painting of a red fox in a birch forest\",\"size\":\"256x256\"}"
120+
extra_hosts: # localhost:host-gateway trick doesn't work with aigw
121+
- "host.docker.internal:host-gateway"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0
1111
github.com/a8m/envsubst v1.4.3
1212
github.com/alecthomas/kong v1.12.1
13+
github.com/andybalholm/brotli v1.2.0
1314
github.com/anthropics/anthropic-sdk-go v1.12.0
1415
github.com/aws/aws-sdk-go-v2 v1.39.1
1516
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1
@@ -83,7 +84,6 @@ require (
8384
github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect
8485
github.com/Microsoft/go-winio v0.6.2 // indirect
8586
github.com/NYTimes/gziphandler v1.1.1 // indirect
86-
github.com/andybalholm/brotli v1.2.0 // indirect
8787
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
8888
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
8989
github.com/avast/retry-go v3.0.0+incompatible // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
425425
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
426426
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
427427
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
428+
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
429+
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
428430
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
429431
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
430432
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

internal/extproc/imagegeneration_processor.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
1919
extprocv3http "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3"
2020
extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
21+
openaisdk "github.com/openai/openai-go/v2"
2122
"google.golang.org/protobuf/types/known/structpb"
2223

2324
"github.com/envoyproxy/ai-gateway/internal/extproc/backendauth"
@@ -27,7 +28,6 @@ import (
2728
"github.com/envoyproxy/ai-gateway/internal/internalapi"
2829
"github.com/envoyproxy/ai-gateway/internal/metrics"
2930
tracing "github.com/envoyproxy/ai-gateway/internal/tracing/api"
30-
openaisdk "github.com/openai/openai-go/v2"
3131
)
3232

3333
// ImageGenerationProcessorFactory returns a factory method to instantiate the image generation processor.
@@ -215,11 +215,11 @@ func (i *imageGenerationProcessorUpstreamFilter) ProcessRequestHeaders(ctx conte
215215
// Start tracking metrics for this request.
216216
i.metrics.StartRequest(i.requestHeaders)
217217
// Set the original model from the request body before any overrides
218-
i.metrics.SetOriginalModel(internalapi.OriginalModel(i.originalRequestBody.Model))
218+
i.metrics.SetOriginalModel(i.originalRequestBody.Model)
219219
// Set the request model for metrics from the original model or override if applied.
220-
reqModel := cmp.Or(i.requestHeaders[i.config.modelNameHeaderKey], string(i.originalRequestBody.Model))
221-
i.metrics.SetRequestModel(internalapi.RequestModel(reqModel))
222-
i.metrics.SetResponseModel(internalapi.ResponseModel(reqModel))
220+
reqModel := cmp.Or(i.requestHeaders[i.config.modelNameHeaderKey], i.originalRequestBody.Model)
221+
i.metrics.SetRequestModel(reqModel)
222+
i.metrics.SetResponseModel(reqModel)
223223

224224
// We force the body mutation in the following cases:
225225
// * The request is a retry request because the body mutation might have happened the previous iteration.
@@ -471,9 +471,9 @@ func (i *imageGenerationProcessorUpstreamFilter) SetBackend(ctx context.Context,
471471
i.headerMutator = headermutator.NewHeaderMutator(b.HeaderMutation, rp.requestHeaders)
472472
// Sync header with backend model so header-derived labels/CEL use the actual model.
473473
if i.modelNameOverride != "" {
474-
i.requestHeaders[i.config.modelNameHeaderKey] = string(i.modelNameOverride)
474+
i.requestHeaders[i.config.modelNameHeaderKey] = i.modelNameOverride
475475
// Update metrics with the overridden model
476-
i.metrics.SetRequestModel(internalapi.RequestModel(i.modelNameOverride))
476+
i.metrics.SetRequestModel(i.modelNameOverride)
477477
}
478478
i.originalRequestBody = rp.originalRequestBody
479479
i.originalRequestBodyRaw = rp.originalRequestBodyRaw
@@ -498,5 +498,5 @@ func parseOpenAIImageGenerationBody(body *extprocv3.HttpBody) (modelName string,
498498
if err := json.Unmarshal(body.Body, &openAIReq); err != nil {
499499
return "", nil, fmt.Errorf("failed to unmarshal body: %w", err)
500500
}
501-
return string(openAIReq.Model), &openAIReq, nil
501+
return openAIReq.Model, &openAIReq, nil
502502
}

internal/extproc/imagegeneration_processor_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import (
1515

1616
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
1717
extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
18+
openaisdk "github.com/openai/openai-go/v2"
1819
"github.com/stretchr/testify/require"
1920

2021
"github.com/envoyproxy/ai-gateway/internal/extproc/translator"
2122
"github.com/envoyproxy/ai-gateway/internal/filterapi"
2223
"github.com/envoyproxy/ai-gateway/internal/llmcostcel"
2324
tracing "github.com/envoyproxy/ai-gateway/internal/tracing/api"
24-
openaisdk "github.com/openai/openai-go/v2"
2525
)
2626

2727
func TestImageGeneration_Schema(t *testing.T) {
@@ -95,7 +95,7 @@ func (m *mockImageGenerationSpan) EndSpanOnError(status int, body []byte) {
9595
m.errBody = string(body)
9696
}
9797

98-
func (m *mockImageGenerationSpan) RecordResponse(resp *openaisdk.ImagesResponse) {
98+
func (m *mockImageGenerationSpan) RecordResponse(_ *openaisdk.ImagesResponse) {
9999
// Mock implementation
100100
}
101101

@@ -390,7 +390,7 @@ type mockImageGenerationTranslator struct {
390390
retImageMetadata translator.ImageGenerationMetadata
391391
}
392392

393-
func (m *mockImageGenerationTranslator) RequestBody(original []byte, req *openaisdk.ImageGenerateParams, forceBodyMutation bool) (*extprocv3.HeaderMutation, *extprocv3.BodyMutation, error) {
393+
func (m *mockImageGenerationTranslator) RequestBody(_ []byte, req *openaisdk.ImageGenerateParams, forceBodyMutation bool) (*extprocv3.HeaderMutation, *extprocv3.BodyMutation, error) {
394394
if m.expRequestBody != nil {
395395
require.Equal(m.t, m.expRequestBody, req)
396396
}
@@ -410,6 +410,7 @@ func (m *mockImageGenerationTranslator) ResponseHeaders(headers map[string]strin
410410
}
411411

412412
func (m *mockImageGenerationTranslator) ResponseBody(headers map[string]string, body io.Reader, endOfStream bool) (*extprocv3.HeaderMutation, *extprocv3.BodyMutation, translator.LLMTokenUsage, translator.ImageGenerationMetadata, error) {
413+
_ = headers
413414
if m.expResponseBody != nil {
414415
bodyBytes, _ := io.ReadAll(body)
415416
require.Equal(m.t, m.expResponseBody.Body, bodyBytes)
@@ -419,13 +420,15 @@ func (m *mockImageGenerationTranslator) ResponseBody(headers map[string]string,
419420
}
420421

421422
func (m *mockImageGenerationTranslator) ResponseError(headers map[string]string, body io.Reader) (*extprocv3.HeaderMutation, *extprocv3.BodyMutation, error) {
423+
_ = headers
424+
_ = body
422425
return m.retHeaderMutation, m.retBodyMutation, m.retErr
423426
}
424427

425428
// imageGenerationBodyFromModel returns a minimal valid image generation request for tests.
426429
func imageGenerationBodyFromModel(t *testing.T, model string) []byte {
427430
t.Helper()
428-
b, err := json.Marshal(&openaisdk.ImageGenerateParams{Model: openaisdk.ImageModel(model), Prompt: "a cat"})
431+
b, err := json.Marshal(&openaisdk.ImageGenerateParams{Model: model, Prompt: "a cat"})
429432
require.NoError(t, err)
430433
return b
431434
}

internal/extproc/mocks_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,47 +410,57 @@ func (m *mockImageGenerationMetrics) StartRequest(map[string]string) {}
410410
func (m *mockImageGenerationMetrics) SetOriginalModel(originalModel string) {
411411
m.model = originalModel
412412
}
413+
413414
func (m *mockImageGenerationMetrics) SetRequestModel(requestModel string) {
414415
m.model = requestModel
415416
}
417+
416418
func (m *mockImageGenerationMetrics) SetResponseModel(responseModel string) {
417419
m.model = responseModel
418420
}
419-
func (m *mockImageGenerationMetrics) SetModel(requestModel, responseModel string) {
421+
422+
func (m *mockImageGenerationMetrics) SetModel(_ string, responseModel string) {
420423
m.model = responseModel
421424
}
422425
func (m *mockImageGenerationMetrics) SetBackend(b *filterapi.Backend) { m.backend = b.Name }
423426
func (m *mockImageGenerationMetrics) RecordTokenUsage(_ context.Context, input, output uint32, _ map[string]string) {
424427
m.tokenUsageCount += int(input + output)
425428
}
429+
426430
func (m *mockImageGenerationMetrics) RecordRequestCompletion(_ context.Context, success bool, _ map[string]string) {
427431
if success {
428432
m.requestSuccessCount++
429433
} else {
430434
m.requestErrorCount++
431435
}
432436
}
437+
433438
func (m *mockImageGenerationMetrics) RecordImageGeneration(_ context.Context, _ int, _ string, _ string, _ map[string]string) {
434439
}
435440

436441
func (m *mockImageGenerationMetrics) RequireRequestFailure(t *testing.T) {
437442
require.Equal(t, 0, m.requestSuccessCount)
438443
require.Equal(t, 1, m.requestErrorCount)
439444
}
445+
440446
func (m *mockImageGenerationMetrics) RequireRequestNotCompleted(t *testing.T) {
441447
require.Equal(t, 0, m.requestSuccessCount)
442448
require.Equal(t, 0, m.requestErrorCount)
443449
}
450+
444451
func (m *mockImageGenerationMetrics) RequireRequestSuccess(t *testing.T) {
445452
require.Equal(t, 1, m.requestSuccessCount)
446453
require.Equal(t, 0, m.requestErrorCount)
447454
}
455+
448456
func (m *mockImageGenerationMetrics) RequireSelectedModel(t *testing.T, model string) {
449457
require.Equal(t, model, m.model)
450458
}
459+
451460
func (m *mockImageGenerationMetrics) RequireSelectedBackend(t *testing.T, backend string) {
452461
require.Equal(t, backend, m.backend)
453462
}
463+
454464
func (m *mockImageGenerationMetrics) RequireTokensRecorded(t *testing.T, count int) {
455465
require.Equal(t, count, m.tokenUsageCount)
456466
}

internal/extproc/translator/imagegeneration_openai_openai.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import (
1414

1515
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
1616
extprocv3 "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
17+
openaisdk "github.com/openai/openai-go/v2"
1718
"github.com/tidwall/sjson"
1819

1920
"github.com/envoyproxy/ai-gateway/internal/internalapi"
2021
tracing "github.com/envoyproxy/ai-gateway/internal/tracing/api"
21-
openaisdk "github.com/openai/openai-go/v2"
2222
)
2323

2424
// NewImageGenerationOpenAIToOpenAITranslator implements [Factory] for OpenAI to OpenAI image generation translation.
@@ -36,7 +36,7 @@ type openAIToOpenAIImageGenerationTranslator struct {
3636
}
3737

3838
// RequestBody implements [ImageGenerationTranslator.RequestBody].
39-
func (o *openAIToOpenAIImageGenerationTranslator) RequestBody(original []byte, req *openaisdk.ImageGenerateParams, forceBodyMutation bool) (
39+
func (o *openAIToOpenAIImageGenerationTranslator) RequestBody(original []byte, _ *openaisdk.ImageGenerateParams, forceBodyMutation bool) (
4040
headerMutation *extprocv3.HeaderMutation, bodyMutation *extprocv3.BodyMutation, err error,
4141
) {
4242
var newBody []byte
@@ -172,7 +172,9 @@ func (o *openAIToOpenAIImageGenerationTranslator) ResponseBody(_ map[string]stri
172172
// extractUsageFromBufferEvent extracts token usage from buffered streaming events.
173173
// This is currently not applicable for image generation as it doesn't use streaming.
174174
// TODO: Implement if streaming support is added for image generation in the future.
175-
func (o *openAIToOpenAIImageGenerationTranslator) extractUsageFromBufferEvent(span tracing.ImageGenerationSpan) LLMTokenUsage {
176-
// Image generation doesn't use streaming, so no token usage to extract
175+
// NOTE: image generation currently does not use streaming; keep for future parity with other translators.
176+
//
177+
//lint:ignore U1000 kept for parity and future use
178+
func (o *openAIToOpenAIImageGenerationTranslator) extractUsageFromBufferEvent(_ tracing.ImageGenerationSpan) LLMTokenUsage {
177179
return LLMTokenUsage{}
178180
}

internal/extproc/translator/imagegeneration_openai_openai_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import (
1010
"encoding/json"
1111
"testing"
1212

13-
"github.com/stretchr/testify/require"
14-
1513
openaisdk "github.com/openai/openai-go/v2"
14+
"github.com/stretchr/testify/require"
1615
)
1716

1817
func TestOpenAIToOpenAIImageTranslator_RequestBody_ModelOverrideAndPath(t *testing.T) {
@@ -32,7 +31,7 @@ func TestOpenAIToOpenAIImageTranslator_RequestBody_ModelOverrideAndPath(t *testi
3231
mutated := bm.GetBody()
3332
var got openaisdk.ImageGenerateParams
3433
require.NoError(t, json.Unmarshal(mutated, &got))
35-
require.Equal(t, "gpt-image-1", string(got.Model))
34+
require.Equal(t, "gpt-image-1", got.Model)
3635
}
3736

3837
func TestOpenAIToOpenAIImageTranslator_RequestBody_ForceMutation(t *testing.T) {
@@ -81,6 +80,6 @@ func TestOpenAIToOpenAIImageTranslator_ResponseBody_OK(t *testing.T) {
8180
require.Equal(t, uint32(0), usage.InputTokens)
8281
require.Equal(t, uint32(0), usage.TotalTokens)
8382
require.Equal(t, 0, metadata.ImageCount)
84-
require.Equal(t, "", metadata.Model)
83+
require.Empty(t, metadata.Model)
8584
require.Equal(t, string(openaisdk.ImagesResponseSize1024x1024), metadata.Size)
8685
}

0 commit comments

Comments
 (0)