Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 29 additions & 18 deletions apps/mobile/src/app/task/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import {
DEFAULT_CLAUDE_EXECUTION_MODE,
getAvailableModes,
} from "@posthog/core/sessions/executionModes";
import {
getComposerModelOptions,
getConfigOptionLabel,
getModelConfigOption,
resolveComposerModelChange,
} from "@posthog/core/task-detail/composerControls";
import {
DEFAULT_GATEWAY_MODEL,
DEFAULT_REASONING_EFFORT,
Expand Down Expand Up @@ -61,12 +67,6 @@ import {
} from "@/features/tasks/composer/attachments/pickers";
import type { PendingAttachment } from "@/features/tasks/composer/attachments/types";
import { DotBackground } from "@/features/tasks/composer/DotBackground";
import {
getMobileModelOptions,
getModelConfigOption,
getModelLabel,
resolveAvailableModel,
} from "@/features/tasks/composer/options";
import { Pill } from "@/features/tasks/composer/Pill";
import { RepositoryPickerInline } from "@/features/tasks/composer/RepositoryPickerInline";
import { SelectSheet } from "@/features/tasks/composer/SelectSheet";
Expand Down Expand Up @@ -129,7 +129,7 @@ export default function NewTaskScreen() {
const restingBottom = bottom("compact");
const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude");
const modelConfigOption = getModelConfigOption(configOptions);
const mobileModelOptions = getMobileModelOptions(modelConfigOption);
const mobileModelOptions = getComposerModelOptions(modelConfigOption);
const {
error,
hasGithubIntegration,
Expand Down Expand Up @@ -212,12 +212,14 @@ export default function NewTaskScreen() {

useEffect(() => {
if (!hasLiveConfig) return;
const availableModel = resolveAvailableModel(modelConfigOption, model);
if (availableModel === model) return;
setModel(availableModel);
if (!isSupportedReasoningEffort("claude", availableModel, reasoning)) {
setReasoning(DEFAULT_REASONING_EFFORT);
}
const next = resolveComposerModelChange({
adapter: "claude",
modelOption: modelConfigOption,
requestedModel: model,
reasoning,
});
if (next.model !== model) setModel(next.model);
if (next.reasoning !== reasoning) setReasoning(next.reasoning);
}, [hasLiveConfig, model, modelConfigOption, reasoning]);
const [creating, setCreating] = useState(false);
const [repoSheetOpen, setRepoSheetOpen] = useState(false);
Expand Down Expand Up @@ -647,7 +649,12 @@ export default function NewTaskScreen() {

<Pill
icon={<Robot size={14} color={themeColors.gray[11]} />}
label={getModelLabel(modelConfigOption, model)}
label={
getConfigOptionLabel(
modelConfigOption.options,
model,
) ?? model
}
onPress={() => setModelSheetOpen(true)}
/>

Expand Down Expand Up @@ -767,10 +774,14 @@ export default function NewTaskScreen() {
title="Model"
value={model}
onChange={(value) => {
setModel(value);
if (!isSupportedReasoningEffort("claude", value, reasoning)) {
setReasoning(DEFAULT_REASONING_EFFORT);
}
const next = resolveComposerModelChange({
adapter: "claude",
modelOption: modelConfigOption,
requestedModel: value,
reasoning,
});
setModel(next.model);
setReasoning(next.reasoning);
}}
onClose={() => setModelSheetOpen(false)}
options={mobileModelOptions.map((modelOption) => ({
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/features/inbox/components/TinderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
inboxStatusLabel,
} from "@posthog/core/inbox/reportPresentation";
import type { RepositoryOption } from "@posthog/core/integrations/repositories";
import { getModelConfigOption } from "@posthog/core/task-detail/composerControls";
import type {
SignalReport,
SignalReportPriority,
Expand All @@ -27,7 +28,6 @@ import {
} from "react-native-safe-area-context";
import { MarkdownText } from "@/features/chat/components/MarkdownText";
import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore";
import { getModelConfigOption } from "@/features/tasks/composer/options";
import { useCloudTaskConfigOptions } from "@/features/tasks/hooks/useCloudTaskConfigOptions";
import type { CreateTaskOptions } from "@/features/tasks/types";
import {
Expand Down
60 changes: 38 additions & 22 deletions apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import {
DEFAULT_CLAUDE_EXECUTION_MODE,
getAvailableModes,
} from "@posthog/core/sessions/executionModes";
import {
getComposerModelOptions,
getConfigOptionLabel,
getModelConfigOption,
resolveComposerModelChange,
resolveComposerPrimaryAction,
} from "@posthog/core/task-detail/composerControls";
import {
DEFAULT_GATEWAY_MODEL,
DEFAULT_REASONING_EFFORT,
type ExecutionMode,
getReasoningEffortOptions,
isSupportedReasoningEffort,
type SupportedReasoningEffort,
} from "@posthog/shared";
import * as Haptics from "expo-haptics";
Expand Down Expand Up @@ -56,12 +62,6 @@ import {
pickPhotoFromLibrary,
} from "./attachments/pickers";
import type { PendingAttachment } from "./attachments/types";
import {
getMobileModelOptions,
getModelConfigOption,
getModelLabel,
resolveAvailableModel,
} from "./options";
import { Pill } from "./Pill";
import { SelectSheet } from "./SelectSheet";

Expand Down Expand Up @@ -188,7 +188,7 @@ export function TaskChatComposer({
const themeColors = useThemeColors();
const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude");
const modelConfigOption = getModelConfigOption(configOptions);
const mobileModelOptions = getMobileModelOptions(modelConfigOption);
const mobileModelOptions = getComposerModelOptions(modelConfigOption);
const [message, setMessage] = useState(() => initialMessage ?? "");
const [attachments, setAttachments] = useState<PendingAttachment[]>([]);
const [attachmentSheetOpen, setAttachmentSheetOpen] = useState(false);
Expand All @@ -206,12 +206,14 @@ export function TaskChatComposer({

useEffect(() => {
if (!hasLiveConfig) return;
const availableModel = resolveAvailableModel(modelConfigOption, model);
if (availableModel === model) return;
onModelChange(availableModel);
if (!isSupportedReasoningEffort("claude", availableModel, reasoning)) {
onReasoningChange(DEFAULT_REASONING_EFFORT);
}
const next = resolveComposerModelChange({
adapter: "claude",
modelOption: modelConfigOption,
requestedModel: model,
reasoning,
});
if (next.model !== model) onModelChange(next.model);
if (next.reasoning !== reasoning) onReasoningChange(next.reasoning);
}, [
hasLiveConfig,
model,
Expand Down Expand Up @@ -239,9 +241,16 @@ export function TaskChatComposer({
const showReasoningPill = reasoningOptions.length > 0;

const hasContent = message.trim().length > 0 || attachments.length > 0;
const canSend = hasContent && !disabled && !isRecording;
const showStop =
!isUserTurn && !canSend && !isRecording && !isTranscribing && !!onStop;
const primaryAction = resolveComposerPrimaryAction({
hasContent,
disabled,
isRecording,
isTranscribing,
canStop: !isUserTurn && !!onStop,
allowSendWhileRunning: true,
});
const canSend = primaryAction === "send";
const showStop = primaryAction === "stop";

const handleSend = () => {
const trimmed = message.trim();
Expand Down Expand Up @@ -410,7 +419,10 @@ export function TaskChatComposer({

<Pill
icon={<Robot size={14} color={themeColors.gray[11]} />}
label={getModelLabel(modelConfigOption, model)}
label={
getConfigOptionLabel(modelConfigOption.options, model) ??
model
}
onPress={() => setModelSheetOpen(true)}
/>

Expand Down Expand Up @@ -486,10 +498,14 @@ export function TaskChatComposer({
title="Model"
value={model}
onChange={(v) => {
onModelChange(v);
if (!isSupportedReasoningEffort("claude", v, reasoning)) {
onReasoningChange(DEFAULT_REASONING_EFFORT);
}
const next = resolveComposerModelChange({
adapter: "claude",
modelOption: modelConfigOption,
requestedModel: v,
reasoning,
});
onModelChange(next.model);
onReasoningChange(next.reasoning);
}}
onClose={() => setModelSheetOpen(false)}
options={mobileModelOptions.map((m) => ({
Expand Down
68 changes: 0 additions & 68 deletions apps/mobile/src/features/tasks/composer/options.test.ts

This file was deleted.

56 changes: 0 additions & 56 deletions apps/mobile/src/features/tasks/composer/options.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ vi.mock("@/lib/posthogApiClient", () => ({
}),
}));

import { getModelConfigOption } from "../composer/options";
import { getModelConfigOption } from "@posthog/core/task-detail/composerControls";
import { useCloudTaskConfigOptions } from "./useCloudTaskConfigOptions";

function createWrapper(queryClient: QueryClient) {
Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/task-detail/composerControls.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";
import { resolveComposerPrimaryAction } from "./composerControls";

describe("resolveComposerPrimaryAction", () => {
it.each([
[{ hasContent: true }, "send"],
[{ canStop: true }, "stop"],
[{ canStop: true, hasContent: true }, "send"],
[{ canStop: true, hasContent: true, allowSendWhileRunning: false }, "stop"],
[{ isRecording: true }, "mic-stop"],
[{}, "mic"],
[{ disabled: true, hasContent: true }, "disabled"],
[{ isTranscribing: true }, "disabled"],
])("derives %s", (overrides, expected) => {
expect(
resolveComposerPrimaryAction({
hasContent: false,
disabled: false,
isRecording: false,
isTranscribing: false,
canStop: false,
allowSendWhileRunning: true,
...overrides,
}),
).toBe(expected);
});
});
Loading
Loading