Skip to content

Commit

Permalink
Support non-strings in valid json
Browse files Browse the repository at this point in the history
  • Loading branch information
ankrgyl committed Dec 15, 2024
1 parent a3ca1cf commit e17b0bb
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 6 deletions.
19 changes: 19 additions & 0 deletions js/json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,25 @@ test("Valid JSON Test", async () => {
uniqueItems: true,
},
},
{
output: { a: "1", b: "1" },
expected: 1,
},
{
output: [{ a: "1" }, { a: "1", b: 22 }],
expected: 1,
},
{
output: 100,
expected: 0,
},
{
// This is technically ambiguous, because it _could_ be the valid parsed JSON value
// or an unparsed, invalid JSON value. However, since structured outputs _only_ return
// JSON values, we can safely assume that any strings are unparsed values.
output: "100",
expected: 0,
},
];

for (const { output, expected, schema } of cases) {
Expand Down
12 changes: 7 additions & 5 deletions js/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,16 @@ export const JSONDiff: ScorerWithPartial<
* A binary scorer that evaluates the validity of JSON output, optionally validating against a
* JSON Schema definition (see https://json-schema.org/learn/getting-started-step-by-step#create).
*/
export const ValidJSON: ScorerWithPartial<string, { schema?: any }> =
makePartial(async ({ output, schema }) => {
export const ValidJSON: ScorerWithPartial<any, { schema?: any }> = makePartial(
async ({ output, schema }) => {
return {
name: "ValidJSON",
score: validJSON(output, schema),
metadata: { schema },
};
}, "ValidJSON");
},
"ValidJSON",
);

async function jsonDiff(
o1: any,
Expand Down Expand Up @@ -151,9 +153,9 @@ const replacer = (key: string, value: any) =>
}, {})
: value;

function validJSON<T>(output: string, schema?: Schema | JSONSchemaType<T>) {
function validJSON<T>(output: any, schema?: Schema | JSONSchemaType<T>) {
try {
const parsed = JSON.parse(output);
const parsed = typeof output === "string" ? JSON.parse(output) : output;

if (schema) {
return validateSchema(parsed, schema);
Expand Down
2 changes: 1 addition & 1 deletion py/autoevals/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _run_eval_sync(self, output, schema=None, **kwargs):

def valid_json(self, output, schema=None):
try:
parsed = json.loads(output)
parsed = json.loads(output) if isinstance(output, str) else output

if schema is not None:
validate(parsed, schema)
Expand Down
23 changes: 23 additions & 0 deletions py/autoevals/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,29 @@ def test_valid_json():
"uniqueItems": True,
},
),
(
{"a": "1", "b": "1"},
1,
None,
),
(
[{"a": "1"}, {"a": "1", "b": 22}],
1,
None,
),
(
100,
0,
None,
),
(
# This is technically ambiguous, because it _could_ be the valid parsed JSON value
# or an unparsed, invalid JSON value. However, since structured outputs _only_ return
# JSON values, we can safely assume that any strings are unparsed values.
"100",
0,
None,
),
]

evaluator = ValidJSON()
Expand Down

0 comments on commit e17b0bb

Please sign in to comment.