Skip to content

Commit

Permalink
refactor: remove Tool.vue component and update error handling
Browse files Browse the repository at this point in the history
Signed-off-by: tylerslaton <[email protected]>
  • Loading branch information
tylerslaton committed Mar 12, 2024
1 parent c6c9876 commit ffc33e6
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 123 deletions.
5 changes: 0 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ services:
- db
environment:
- HOST=0.0.0.0
- NUXT_PUBLIC_PARSER_URL=http://localhost:8080
- NUXT_PARSER_URL=http://parser:8080
- NUXT_DB_URL=postgres://postgres:postgres@db:5432/pkg-gptscript
db:
Expand All @@ -24,8 +23,6 @@ services:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: pkg-gptscript
volumes:
- postgres_data:/var/lib/postgresql/data/
parser:
build:
context: ./parser
Expand All @@ -34,5 +31,3 @@ services:
- "8080:8080"
volumes:
- ./parser:/app
volumes:
postgres_data:
3 changes: 0 additions & 3 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,5 @@ export default defineNuxtConfig({
runtimeConfig: {
databaseUrl: '', // NUXT_DATABASE_URL
parserUrl: '', // NUXT_PARSER_URL
public: {
parserUrl: '' // NUXT_PUBLIC_PARSER_URL
}
},
})
2 changes: 1 addition & 1 deletion src/app.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div>
<Navigation />
<Navigation class="fixed w-full top-0 z-50"/>
<NuxtPage />
</div>
</template>
14 changes: 14 additions & 0 deletions src/components/Error.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<template>
<div class="flex flex-col items-center justify-center h-screen">
<h1 class="text-4xl font-bold mb-4">{{ props.title }}</h1>
<p class="text-lg"> {{ props.message }}</p>
<router-link to="/" class="mt-4 text-blue-500 hover:underline">Go back to home</router-link>
</div>
</template>

<script setup lang="ts">
const props = defineProps({
title: String,
message: String,
});
</script>
17 changes: 7 additions & 10 deletions src/components/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
<template>
<div class="sidebar text-gray-600">
<div v-for="header in headers" :key="header.id" class="mb-4 overflow-auto">
<a :href="`#${header.id}`">{{ header.innerHTML || "root" }}</a>
<div v-for="header in props.headers" :key="'sidebar-'+header" class="mb-4 overflow-auto">
<a :href="`#tool-${header}`">{{ header || "root" }}</a>
</div>
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
const headers = ref([]);
onMounted(() => {
fetchHeaders();
const props = defineProps({
headers: {
type: Array,
required: true
}
});
const fetchHeaders = () => headers.value = Array.from(document.querySelectorAll('h2'));
</script>
45 changes: 0 additions & 45 deletions src/components/Tool.vue

This file was deleted.

60 changes: 45 additions & 15 deletions src/pages/[...slug].vue
Original file line number Diff line number Diff line change
@@ -1,33 +1,63 @@
<template>
<div class="flex">
<div class="hidden md:block border-r border-gray-200">
<Sidebar class="mx-20 sticky top-8 pt-10"/>
</div>
<div class="pt-10 mx-auto w-auto sm:mx-20">
<Tool :owner="owner" :path="path"/>
<div>
<div v-if="!error.status" class="flex">
<div class="hidden md:block border-r border-gray-200">
<Sidebar :headers="tools.map(tool => tool.name)" class="mt-28 sticky top-28 px-20"/>
</div>

<div class="prose my-28 mx-20">
<h1 class="mb-0">{{ path }}</h1>
<a :href="githubURL" target="_blank" class="text-blue-500 underline">{{ githubURL }}</a>
<div v-for="tool in tools" :key="tool.id">
<h2 :id="'tool-' + tool.name">{{ tool.name }}</h2>
<p class="">{{ tool.description }}</p>

<h3 :id="tool+ '-arguments'">Arguments</h3>
<table>
<tbody>
<tr v-for="(properties, arg) in tool.arguments?.properties" :key="arg">
<td class="font-semibold">{{ arg }}</td>
<td class="">{{ properties.description }}</td>
</tr>
</tbody>
</table>

<h3 v-if="tool.tools && tool.tools.length" :id="tool.name + 'tools-used'">Tools used</h3>
<div v-if="tool.tools && tool.tools.length">
<p v-for="(usedTool) in tool.tools" :key="usedTool">{{ usedTool }}</p>
</div>
</div>
</div>
</div>

<Error class="flex flex-col items-center justify-center h-screen" v-else :title="`${error.status}`" :message="error.message"/>
</div>
</template>

<script setup lang="ts">
// todo: this calls the endpoint twice. the first time fails and the second succeeds.
// at the time of writing this comment, I don't know why. This likely has something
// to do with the lifecycle of the component since putting this behind onMounted
// causes there to be no data at all.
import type { Tool } from '@/lib/types';
import { ref } from "vue";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const owner = ref("");
const path = ref("");
const tools = ref([] as Tool[])
const error = ref({status: 0, message: ''});
const githubURL = route.path.replace(/^\//, "");
const validRepo = /^(https?:\/\/)?(www\.)?github\.com\/[\w-]+\/[\w-]+$/.test(githubURL);
[owner.value, path.value] = githubURL.split("/").slice(-2);
if (!validRepo) {
router.push({ path: "/404" });
}
onMounted(async () => {
document.title = `${owner.value}/${path.value}`;
const toolAPIResponse = await fetch(`/api/github.com/${owner.value}/${path.value}`, { method: 'POST' });
if (!toolAPIResponse.ok) {
error.value = { status: toolAPIResponse.status, message: toolAPIResponse.statusText };
return;
}
[owner.value, path.value] = githubURL.split("/").slice(-2);
const toolAPIResponseJSON = await toolAPIResponse.json();
tools.value = toolAPIResponseJSON as Tool[];
});
</script>
80 changes: 36 additions & 44 deletions src/server/api/[...slug].post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,51 @@ import * as db from '@/lib/db';
import type { Tool } from '@/lib/types';

export default defineEventHandler(async (event) => {
// NOTE: this path may be incorrect
// grab the github URL from the path and check that it is valid
const url = event.path.replace(/^\/api\//, "");

// should update this to account for subpaths
if (!/^(https?:\/\/)?(www\.)?github\.com\/[\w-]+\/[\w-]+$/.test(url)) {
return {
status: 404,
body: "path not found"
};
}

const query = getQuery(event)
const tools = await getOrIndexRepo(url, query.force === "true");

if (tools.length === 0) {
return {
status: 404,
body: "no tools found"
};
throw createError({
statusCode: 400,
statusMessage: "Invalid URL"
});
}

return {
status: 200,
body: tools
};
})

const getOrIndexRepo = async (url: string, force: boolean): Promise<Tool[]> => {
// todo:
// - look for a tool.gpt if it exists and if it doesn't look if the file specified is a tool file
const tools = await db.getToolsForUrl(url);
let tools = await db.getToolsForUrl(url);

if (tools.length > 0 && !force) {
// if the tool is already indexed and force is not true, return the tool
if (tools.length > 0 && !getQuery(event).force) {
setResponseHeader(event, "Content-Type", "application/json");
return tools;
}

const [owner, repo] = url.split("/").slice(-2);

// Tool file wasn't found in the db or force is true so we need to try and index the URL
try {
const toolResponse = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/main/tool.gpt`);
const parserResponse = await fetch(useRuntimeConfig().parserUrl as string, {
method: 'POST',
body: await toolResponse.text(),
headers: {
'Content-Type': 'text/plain',
}
// fetch the tool.gpt file from github
const toolResponse = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/main/tool.gpt`);
if (!toolResponse.ok) {
throw createError({
statusCode: toolResponse.status,
statusMessage: await toolResponse.text()
});
}

// parse the tool.gpt file into a JSON object
const parserResponse = await fetch(useRuntimeConfig().parserUrl, {
method: 'POST',
body: await toolResponse.text(),
headers: { 'Content-Type': 'text/plain' }
});

// if the parser fails, return the error
if (!parserResponse.ok) {
throw createError({
statusCode: parserResponse.status,
statusMessage: await parserResponse.text()
});
const parserResponseBody = await parserResponse.text();
const insertedTools = await db.upsertToolForUrl(url, JSON.parse(parserResponseBody) as Tool[]);
return insertedTools;
} catch (error) {
console.error(error);
}
return [];
}

// upsert the tool into the database and return the tool
const parserResponseBody = await parserResponse.text();
setResponseHeader(event, "Content-Type", "application/json");
return await db.upsertToolForUrl(url, JSON.parse(parserResponseBody) as Tool[]);
})

0 comments on commit ffc33e6

Please sign in to comment.