Skip to content

@effect/ai-openai schema incorrect for some OpenRouter models #4702

@denishsharma

Description

@denishsharma

What version of Effect is running?

3.14.6

What steps can reproduce the bug?

// Set apiUrl to 'https://openrouter.ai/api/v1'

yield* openAI.client.createChatCompletion({
  model: 'deepseek/deepseek-r1-distill-llama-70b:free', // model from OpenRouter
  messages: [
    {
      role: 'user',
      content: 'Why isn't this working?',
    },
  ],
});

What is the expected behavior?

The effect should result in response.

What do you see instead?

Struct (Encoded side) <-> Struct (Type side)
└─ Encoded side transformation failure
   └─ Struct (Encoded side)
      └─ ["choices"]
         └─ ReadonlyArray<{
              readonly finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | "function_call";
              readonly index: Int;
              readonly message: (Struct (Encoded side) <-> Struct (Type side));
              readonly logprobs: {
                readonly content: ReadonlyArray<{
                  readonly token: string;
                  readonly logprob: number;
                  readonly bytes: ReadonlyArray<Int> | null;
                  readonly top_logprobs: ReadonlyArray<{
                    readonly token: string;
                    readonly logprob: number;
                    readonly bytes: ReadonlyArray<Int> | null;
                  }>
                }> | null;
                readonly refusal: ReadonlyArray<{
                  readonly token: string;
                  readonly logprob: number;
                  readonly bytes: ReadonlyArray<Int> | null;
                  readonly top_logprobs: ReadonlyArray<{
                    readonly token: string;
                    readonly logprob: number;
                    readonly bytes: ReadonlyArray<Int> | null;
                  }>
                }> | null;
              } | null;
            }>
            └─ [0]
               └─ {
                    readonly finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | "function_call";
                    readonly index: Int;
                    readonly message: (Struct (Encoded side) <-> Struct (Type side));
                    readonly logprobs: {
                      readonly content: ReadonlyArray<{
                        readonly token: string;
                        readonly logprob: number;
                        readonly bytes: ReadonlyArray<Int> | null;
                        readonly top_logprobs: ReadonlyArray<{
                          readonly token: string;
                          readonly logprob: number;
                          readonly bytes: ReadonlyArray<Int> | null;
                        }>
                      }> | null;
                      readonly refusal: ReadonlyArray<{
                        readonly token: string;
                        readonly logprob: number;
                        readonly bytes: ReadonlyArray<Int> | null;
                        readonly top_logprobs: ReadonlyArray<{
                          readonly token: string;
                          readonly logprob: number;
                          readonly bytes: ReadonlyArray<Int> | null;
                        }>
                      }> | null;
                    } | null;
                  }
                  └─ ["logprobs"]
                     └─ {
                          readonly content: ReadonlyArray<{
                            readonly token: string;
                            readonly logprob: number;
                            readonly bytes: ReadonlyArray<Int> | null;
                            readonly top_logprobs: ReadonlyArray<{
                              readonly token: string;
                              readonly logprob: number;
                              readonly bytes: ReadonlyArray<Int> | null;
                            }>
                          }> | null;
                          readonly refusal: ReadonlyArray<{
                            readonly token: string;
                            readonly logprob: number;
                            readonly bytes: ReadonlyArray<Int> | null;
                            readonly top_logprobs: ReadonlyArray<{
                              readonly token: string;
                              readonly logprob: number;
                              readonly bytes: ReadonlyArray<Int> | null;
                            }>
                          }> | null;
                        } | null
                        ├─ Expected {
                            readonly content: ...;
                            readonly refusal: ...;
                          }, actual -0.0000017881393
                        └─ Expected null, actual -0.0000017881393

Additional information

For some OpenRouter models, logprobs return number as well.

Also in response, some model responds with no id which matches the Response schema from the OpenRouter (https://openrouter.ai/docs/api-reference/chat-completion)

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions