Skip to content

Commit

Permalink
make CLI general
Browse files Browse the repository at this point in the history
  • Loading branch information
harrysolovay committed Oct 28, 2024
1 parent 8183b2c commit cdeb9e0
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 103 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.env
FEEDBACK.md
target
9 changes: 3 additions & 6 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@
"tasks": {
"mod": "deno run -A https://deno.land/x/[email protected]/mod.ts && dprint fmt",
"test": "deno test -A",
"test:update": "deno task test -- --update"
"test:update": "deno task test -- --update",
"openai:check": "deno run -A main.ts -i . -o FEEDBACK.md --o1"
},
"imports": {
"@deno/dnt": "jsr:@deno/dnt@^0.41.3",
"@std/assert": "jsr:@std/assert@1",
"@std/async": "jsr:@std/async@^1.0.7",
"@std/cli": "jsr:@std/cli@^1.0.6",
"@std/dotenv": "jsr:@std/dotenv@^0.225.2",
"@std/fs": "jsr:@std/fs@^1.0.4",
"@std/path": "jsr:@std/path@^1.0.6",
"@std/testing": "jsr:@std/testing@^1.0.3",
"openai": "npm:openai@^4.68.1"
},
"scopes": {
"./examples/": {
"structured-outputs": "./mod.ts"
}
},
"lint": {
"rules": {
"include": ["ban-untagged-todo", "guard-for-in"],
Expand Down
10 changes: 10 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions examples/CodeCritique.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as T from "../mod.ts"

export class Suggestion extends T.object(
"A container for information related to a replacement suggestion.",
{
file: T.string(
"The file containing the span to be potentially replaced.",
),
startLine: T.number(
"The line on which the potentially-to-be-replaced span starts.",
),
endLine: T.number(
"The line on which the potentially-to-be-replaced span ends.",
),
what: T.string(
"The suggestion for the replacement.",
),
why: T.string(
"The reason for suggesting the replacement",
),
code: T.string(
"The replacement code.",
),
},
) {}

export default class extends T.object(
undefined,
{
suggestions: T.array(
"A list of replacement suggestions.",
Suggestion,
),
},
) {}
2 changes: 1 addition & 1 deletion examples/character.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as T from "structured-outputs"
import * as T from "../mod.ts"

export const Sex = T.union(
"The biological sex of the character.",
Expand Down
90 changes: 0 additions & 90 deletions examples/code_critique.ts

This file was deleted.

103 changes: 103 additions & 0 deletions main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { parseArgs } from "@std/cli"
import * as T from "./mod.ts"
import "@std/dotenv/load"
import { assert } from "@std/assert"
import * as fs from "@std/fs"
import * as path from "@std/path"
import Openai from "openai"
import { displayCompletion } from "./util/displayCompletion.ts"

let {
input,
instructions,
output,
"models-module": modelsModule,
"root-model": rootModel,
exclude,
o1,
"line-numbers": lineNumbers,
} = parseArgs(Deno.args, {
boolean: ["o1", "line-numbers"],
string: ["instructions", "output", "models-module", "root-model"],
collect: ["input", "exclude"],
alias: {
input: "i",
output: "o",
"models-module": "m",
"root-model": "r",
exclude: "e",
},
default: {
instructions: "You're an expert systems engineer. Provide feedback on the following.",
"root-model": "default",
exclude: [
".git",
"node_modules",
"target",
"deno.lock",
".env",
],
},
})

assert(input.length)

const documents = await Promise.all(
(input as string[]).map(async function load(spec: string) {
const p = path.resolve(spec)
const include: string[] = []
const stat = await Deno.stat(p)
if (stat.isDirectory) {
const skip = (exclude as string[]).map((v) => path.globToRegExp(path.join(Deno.cwd(), v)))
for await (
const { path: srcPath } of fs.walk(p, {
includeDirs: false,
followSymlinks: true,
skip,
})
) include.push(srcPath)
} else include.push(p)
return await Promise.all(
include.map((p) =>
Deno.readTextFile(p).then((contents) => [path.relative(Deno.cwd(), p), contents] satisfies [string, string])
),
)
}),
).then((v) => v.flat())

while (documents.length) {
const [filename, contents] = documents.shift()!
instructions = instructions
+ `\n\n\`${filename}\`\n\n${
lineNumbers
? contents.split("\n").map((v, i) => `${i + 1} | ${v}`).join("\n")
: contents
}`
}

const response_format = await (async () => {
if (modelsModule) {
const models = await import(modelsModule)
if (!models[rootModel]) throw 0
return T.ResponseFormat(
"suggest_replacements",
"offer replacement suggestions for specific spans of the provided files.",
models,
rootModel,
)
}
})()

const openai = new Openai({ apiKey: Deno.env.get("OPENAI_API_KEY") })
const completion = await openai.chat.completions.create({
model: o1 ? "o1-preview" : "gpt-4o",
messages: [
{
role: "user",
content: instructions,
},
],
response_format,
})

await displayCompletion(completion, output)
11 changes: 11 additions & 0 deletions util/displayCompletion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type Openai from "openai"

export async function displayCompletion(
{ usage, choices: [firstChoice] }: Openai.Chat.Completions.ChatCompletion,
dest?: string,
) {
const { finish_reason, message } = firstChoice!
const { content } = message
console.log(JSON.stringify({ usage, finish_reason, content }, null, 2))
if (dest && content) await Deno.writeTextFile(dest, content)
}
6 changes: 0 additions & 6 deletions util/logCompletion.ts

This file was deleted.

0 comments on commit cdeb9e0

Please sign in to comment.