Skip to content

Commit

Permalink
metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
actualwitch committed Jan 14, 2025
1 parent c5dbd16 commit a29275c
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 128 deletions.
Binary file modified bun.lockb
Binary file not shown.
22 changes: 19 additions & 3 deletions src/atoms/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { divergentAtom, entangledAtom } from "../utils/entanglement";
import { getRealm, hasBackend } from "../utils/realm";
import { author } from "../const";
import type { _Message, SerialExperiment, ExperimentWithMeta, Message } from "../types";
import type { ProviderType } from "../feature/inference/atoms";
import { modelLabels, type ProviderType } from "../feature/inference/types";

export type LayoutType = "mobile" | "desktop";
export const layoutAtom = atom<LayoutType>();
Expand Down Expand Up @@ -101,6 +101,11 @@ export const storeAtom = divergentAtom(
);

export const experimentAtom = entangledAtom("experiment", atom<Message[]>([]));
export const selectedProviderAtom = entangledAtom("selected-provider", focusAtom(storeAtom, (o) => o.prop("selectedProvider")));
export const modelAtom = entangledAtom(
"model",
focusAtom(storeAtom, (o) => o.prop("selectedModel")),
);

export const experimentIdsAtom = entangledAtom(
"experimentIds",
Expand Down Expand Up @@ -150,7 +155,7 @@ export const createExperiment = atom(
null,
(get, set, messages?: Message[], id?: string, runId?: string): ExperimentCursor => {
const exp = get(experimentsAtom) ?? {};

const parent = get(parentAtom);
id ??= parent;

Expand All @@ -159,9 +164,20 @@ export const createExperiment = atom(
const thisExperiment = exp[id] ?? {};
runId ??= String(Object.keys(thisExperiment).length + 1);

const provider = get(selectedProviderAtom);
const model = get(modelAtom);
const modelName = provider && model && modelLabels[provider][model];

set(experimentsAtom, (prev) => ({
...prev,
[id]: { ...thisExperiment, [runId]: messages ?? [] },
[id]: {
...thisExperiment,
[runId]: {
messages: messages ?? [],
timestamp: new Date().toISOString(),
model: modelName,
},
},
}));

return { id, runId };
Expand Down
45 changes: 22 additions & 23 deletions src/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ import styled from "@emotion/styled";
import { atom, useAtom, useAtomValue } from "jotai";
import { type CSSProperties, type ReactNode, useEffect, useMemo, useRef } from "react";
import { TRIANGLE } from "../const";
import {
type Store,
experimentLayoutAtom,
isDarkModeAtom,
templatesAtom,
} from "../atoms/common";
import { type Store, experimentLayoutAtom, isDarkModeAtom, templatesAtom } from "../atoms/common";
import { isRunningAtom } from "../feature/inference/atoms";
import { bs } from "../style";
import type { WithDarkMode } from "../style/darkMode";
Expand All @@ -17,7 +12,7 @@ import { deepEqual } from "../utils";
import { useHandlers } from "../utils/keyboard";
import { useScrollToTop } from "../utils/scroll";
import { View, collapsedAtom } from "./view";
import type { _Message, Message } from "../types";
import type { _Message, Experiment, ExperimentWithMeta, Message } from "../types";

const baseHeight = bs(6);
export const ChatContainer = styled.div<WithDarkMode>`
Expand Down Expand Up @@ -67,7 +62,8 @@ export const MessageComponent = styled.article<{
isSelected?: boolean;
isDarkMode?: boolean;
experimentLayout: Store["experimentLayout"];
}>(({ role, ioType, contentType, isSelected, isDarkMode, experimentLayout }) => {
name?: string
}>(({ name, role, ioType, contentType, isSelected, isDarkMode, experimentLayout }) => {
const fromServer = ioType === "output";
const align = getAlign(fromServer, experimentLayout);
const styles: SerializedStyles[] = [
Expand All @@ -84,7 +80,7 @@ export const MessageComponent = styled.article<{
}
&:before {
content: "${[contentType, role].filter(Boolean).join(` ${TRIANGLE} `)}";
content: "${[contentType, name ?? role].filter(Boolean).join(` ${TRIANGLE} `)}";
position: absolute;
${align}: 0;
transform-origin: ${align};
Expand Down Expand Up @@ -175,7 +171,7 @@ export const MessageComponent = styled.article<{
type Path = [number] | [number, "content"];
export const selectionAtom = atom<Path | null>(null);

function hasMessages(obj: _Message | { messages: Message[] }): obj is { messages: Message[] } {
function hasMessages(obj: _Message | ExperimentWithMeta): obj is ExperimentWithMeta{
return Object.hasOwn(obj, "messages");
}

Expand Down Expand Up @@ -285,6 +281,7 @@ export const ChatMessage = ({ message: _message, index }: { message: Message; in
ref={ref}
role={message.role}
contentType={contentType}
name={message.name}
isSelected={isSelected}
isDarkMode={isDarkMode}
experimentLayout={experimentLayout}
Expand All @@ -311,18 +308,29 @@ const Banner = styled.div`
`;

export function ChatPreview({
messages,
experiment,
autoScroll,
autoScrollAnchor = "first",
autoScrollAnchor = "first"
}: {
messages: Message[];
experiment: Experiment;
autoScroll?: boolean;
autoScrollAnchor?: "first" | "last";
}) {
const Anchor = useScrollToTop("top", [messages.length, autoScroll, autoScrollAnchor]);
const [selection, setSelection] = useAtom(selectionAtom);
const [isDarkMode] = useAtom(isDarkModeAtom);
const [isRunning] = useAtom(isRunningAtom);
const computedMessages = useMemo(() => {
const messages = Array.isArray(experiment) ? experiment : experiment.messages;
const keyed = messages.map((message, index) => {
return { ...message, key: index };
});
const reversed = [...keyed].reverse();
if (isRunning && !reversed[0].fromServer) {
reversed.unshift({ role: "assistant", content: "...", fromServer: true, key: -1 });
}
return reversed;
}, [experiment, isRunning]);
const Anchor = useScrollToTop("top", [computedMessages.length, autoScroll, autoScrollAnchor]);

useHandlers({
Escape: () => {
Expand All @@ -334,15 +342,6 @@ export function ChatPreview({
return () => void setSelection(null);
}, []);

const computedMessages = useMemo(() => {
const keyed = messages.map((message, index) => ({ ...message, key: index }));
const reversed = [...keyed].reverse();
if (isRunning && !reversed[0].fromServer) {
reversed.unshift({ role: "assistant", content: "...", fromServer: true, key: -1 });
}
return reversed;
}, [messages, isRunning]);

if (computedMessages.length === 0) {
return <Banner></Banner>;
}
Expand Down
8 changes: 5 additions & 3 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSetAtom } from "jotai";
import { useNavigate } from "react-router";
import { experimentAtom, parentAtom, type Message } from "../atoms/common";
import { experimentAtom, parentAtom } from "../atoms/common";
import { Button } from "../style";
import type { Experiment } from "../types";

export const ForkButton = ({ experiment, parent }: { experiment?: Message[]; parent?: string }) => {
export const ForkButton = ({ experiment, parent }: { experiment?: Experiment; parent?: string }) => {
const setExoeriment = useSetAtom(experimentAtom);
const setParent = useSetAtom(parentAtom);
const navigate = useNavigate();
Expand All @@ -12,7 +13,8 @@ export const ForkButton = ({ experiment, parent }: { experiment?: Message[]; par
type="submit"
onClick={() => {
if (!experiment) return;
setExoeriment(experiment);
const messages = Array.isArray(experiment) ? experiment : experiment.messages;
setExoeriment(messages);
if (parent) setParent(parent);
navigate("/");
}}
Expand Down
11 changes: 7 additions & 4 deletions src/const/dynamic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { version } from ".";
import { getDebug, getReleaseHash } from "./_macro" with { type: "macro" };
// import { getDebug, getReleaseHash } from "./_macro" with { type: "macro" };

export const DEBUG = getDebug();
const hash = await getReleaseHash();
export const VERSION = `${version}-${hash}`;
// export const DEBUG = getDebug();
// const hash = await getReleaseHash();
// export const VERSION = `${version}-${hash}`;

export const DEBUG = false;
export const VERSION = version;
77 changes: 8 additions & 69 deletions src/feature/inference/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,16 @@ import { atom } from "jotai";
import { focusAtom } from "jotai-optics";
import OpenAI from "openai";
import type { ChatCompletionCreateParamsStreaming } from "openai/resources/index.mjs";
import { Literal, Union } from "runtypes";

import { experimentToAnthropic } from "./adapters/anthropic";
import { experimentToMistral } from "./adapters/mistral";
import { experimentToOpenai } from "./adapters/openai";
import { spawn } from "../../utils";
import { entangledAtom } from "../../utils/entanglement";
import { hasBackend } from "../../utils/realm";
import {createExperiment, experimentAtom, parentAtom, storeAtom, tokensAtom } from "../../atoms/common";
import {createExperiment, experimentAtom, modelAtom, selectedProviderAtom, storeAtom, tokensAtom } from "../../atoms/common";
import type { Message } from "../../types";

export function withIds<T extends string>(items: T[] | readonly T[]) {
return items.map((name) => ({
id: name,
name,
}));
}
export const providerTypes = ["anthropic", "mistral", "openai"] as const;
export type ProviderType = (typeof providerTypes)[number];
export const providers = withIds(providerTypes);
export const providerLabels = {
anthropic: "Anthropic",
mistral: "Mistral",
openai: "OpenAI",
} satisfies { [K in ProviderType]: string };
import { modelLabels, modelOptions, providerLabels, type ProviderType } from "./types";

export const availableProviderOptionsAtom = atom((get) => {
const tokens = get(tokensAtom);
Expand All @@ -45,66 +30,16 @@ export const availableProviderOptionsAtom = atom((get) => {
}));
});

export const selectedProviderAtom = entangledAtom("selected-provider", focusAtom(storeAtom, (o) => o.prop("selectedProvider")));

export const OpenAIModel = Union(
Literal("gpt-4o"),
Literal("gpt-4o-mini"),
Literal("gpt-4"),
Literal("gpt-4-turbo"),
Literal("o1"),
Literal("o1-preview"),
Literal("o1-mini"),
);
export const AnthropicModel = Union(
Literal("claude-3-5-sonnet-latest"),
Literal("claude-3-5-haiku-latest"),
Literal("claude-3-opus-latest"),
);
export const MistralModel = Union(
Literal("mistral-large-latest"),
Literal("mistral-medium-latest"),
Literal("mistral-small-latest"),
);

export const modelOptions = {
openai: OpenAIModel.alternatives.map((model) => model.value),
anthropic: AnthropicModel.alternatives.map((model) => model.value),
mistral: MistralModel.alternatives.map((model) => model.value),
};
export const modelLabels = {
openai: {
"gpt-4o": "GPT-4o",
"gpt-4o-mini": "GPT-4o Mini",
"gpt-4": "GPT-4",
"gpt-4-turbo": "GPT-4 Turbo",
"o1": "O1",
"o1-preview": "O1 Preview",
"o1-mini": "O1 Mini",
},
anthropic: {
"claude-3-5-sonnet-latest": "Claude 3.5 Sonnet",
"claude-3-5-haiku-latest": "Claude 3.5 Haiku",
"claude-3-opus-latest": "Claude 3 Opus",
},
mistral: {
"mistral-large-latest": "Mistral Large",
"mistral-medium-latest": "Mistral Medium",
"mistral-small-latest": "Mistral Small",
},
} as const;

export const modelOptionsAtom = atom((get) => {
const provider = get(selectedProviderAtom);
if (!provider) return [];
return modelOptions[provider].map((model) => ({
id: model,
name: modelLabels[provider][model],
name: modelLabels?.[provider]?.[model] ?? model,
}));
});

export const tempAtom = entangledAtom("temp", atom(0.0));
export const modelAtom = entangledAtom("model", focusAtom(storeAtom, (o) => o.prop("selectedModel")));
export const isRunningAtom = entangledAtom("is running", atom(false));

export const modelSupportsTemperatureAtom = atom((get) => {
Expand Down Expand Up @@ -155,7 +90,11 @@ export const runInferenceAtom = entangledAtom(
break;
}
}
set(saveExperimentAtom);
const experiment: Message[] = get(experimentAtom);
if (experiment.length > 0) {
const model = get(modelAtom);
set(createExperiment, experiment);
}
} catch (e) {
console.error(e);
} finally {
Expand Down
63 changes: 63 additions & 0 deletions src/feature/inference/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Literal, Union } from "runtypes";

export function withIds<T extends string>(items: T[] | readonly T[]) {
return items.map((name) => ({
id: name,
name,
}));
}
export const providerTypes = ["anthropic", "mistral", "openai"] as const;
export type ProviderType = (typeof providerTypes)[number];
export const providers = withIds(providerTypes);
export const providerLabels = {
anthropic: "Anthropic",
mistral: "Mistral",
openai: "OpenAI",
} satisfies { [K in ProviderType]: string };

export const OpenAIModel = Union(
Literal("gpt-4o"),
Literal("gpt-4o-mini"),
Literal("gpt-4"),
Literal("gpt-4-turbo"),
Literal("o1"),
Literal("o1-preview"),
Literal("o1-mini"),
);
export const AnthropicModel = Union(
Literal("claude-3-5-sonnet-latest"),
Literal("claude-3-5-haiku-latest"),
Literal("claude-3-opus-latest"),
);
export const MistralModel = Union(
Literal("mistral-large-latest"),
Literal("mistral-medium-latest"),
Literal("mistral-small-latest"),
);

export const modelOptions = {
openai: OpenAIModel.alternatives.map((model) => model.value),
anthropic: AnthropicModel.alternatives.map((model) => model.value),
mistral: MistralModel.alternatives.map((model) => model.value),
};
export const modelLabels = {
openai: {
"gpt-4o": "GPT-4o",
"gpt-4o-mini": "GPT-4o Mini",
"gpt-4": "GPT-4",
"gpt-4-turbo": "GPT-4 Turbo",
"o1": "O1",
"o1-preview": "O1 Preview",
"o1-mini": "O1 Mini",
},
anthropic: {
"claude-3-5-sonnet-latest": "Claude 3.5 Sonnet",
"claude-3-5-haiku-latest": "Claude 3.5 Haiku",
"claude-3-opus-latest": "Claude 3 Opus",
},
mistral: {
"mistral-large-latest": "Mistral Large",
"mistral-medium-latest": "Mistral Medium",
"mistral-small-latest": "Mistral Small",
},
} as const;
Loading

0 comments on commit a29275c

Please sign in to comment.