mirror of
https://github.com/vegu-ai/talemate.git
synced 2026-09-01 19:48:52 +02:00
Refactor advance time scene tool menu
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
- "Asset Exists Node: Added an allow_partial option that enables startswith matching instead of exact asset ID matching."
|
||||
- "Prompt From Template Node: Added a dedupe property (default on) to control prompt deduplication. Useful for templates with structured repeated content like beat listings that should not be deduplicated."
|
||||
- "Gemma 4 Prompt Template: Added a prompt template for Google's Gemma 4 model family, with support for toggling thinking mode via the reasoning tokens setting."
|
||||
- "Advance Time: Moved time tools into a dedicated sub-component with grouped presets (Minutes, Hours, Days, Weeks, Months, Years) and a custom time dialog for arbitrary durations."
|
||||
fixes:
|
||||
- "Message Regeneration: Fixed a bug where regenerating a message that fails (e.g., missing agent or function) would permanently remove the original message from the scene. The original message is now restored to both history and UI on failure, and unhandled exceptions during regeneration are caught."
|
||||
- "Anthropic Client: Fixed generation error caused by missing required max_tokens parameter when response length capping is disabled. Now defaults to the model's API output token limit."
|
||||
@@ -24,5 +25,6 @@
|
||||
- "Encryption: Fixed API keys in agent action configs (e.g., OpenAI-compatible TTS, visual backends) being stored in plaintext. The encryption walker now handles the nested AgentActionConfig structure where the key is wrapped in a {value: ...} dict."
|
||||
- "Agent Config: Stopped persisting unified_api_key fields to config.yaml. These are static reference pointers (e.g., 'openai.api_key') defined in code and never change, so saving them was unnecessary."
|
||||
- "Contextual Generate: Fixed list-type generation prefilling an empty line after '1.', which caused some LLMs to produce empty lists."
|
||||
- "Advance Time: Fixed toolbar time advancement being completely non-functional due to a missing websocket handler. Also fixed invalid ISO 8601 duration strings for the 1 week and 2 weeks options."
|
||||
improvements:
|
||||
- "Prompts Tab Alert: The top-level Prompts tab now shows a warning icon when any active template overrides are outdated, so you can spot them without opening the tab."
|
||||
@@ -3,6 +3,7 @@ import pydantic
|
||||
from talemate.instance import get_agent
|
||||
from talemate.server.websocket_plugin import Plugin
|
||||
from talemate.status import set_loading
|
||||
from talemate.util.time import amount_unit_to_iso8601_duration
|
||||
|
||||
__all__ = [
|
||||
"WorldStateWebsocketHandler",
|
||||
@@ -18,6 +19,24 @@ class SummarizeAndPinPayload(pydantic.BaseModel):
|
||||
num_messages: int = 5
|
||||
|
||||
|
||||
class AdvanceTimePayload(pydantic.BaseModel):
|
||||
duration: str | None = None
|
||||
amount: int | None = None
|
||||
unit: str | None = None
|
||||
|
||||
@pydantic.model_validator(mode="after")
|
||||
def check_duration_or_amount_unit(self):
|
||||
if not self.duration and (self.amount is None or not self.unit):
|
||||
raise ValueError("Either 'duration' or 'amount' + 'unit' must be provided.")
|
||||
return self
|
||||
|
||||
@property
|
||||
def iso_duration(self) -> str:
|
||||
if self.duration:
|
||||
return self.duration
|
||||
return amount_unit_to_iso8601_duration(self.amount, self.unit)
|
||||
|
||||
|
||||
class WorldStateWebsocketHandler(Plugin):
|
||||
router = "world_state_agent"
|
||||
|
||||
@@ -47,3 +66,9 @@ class WorldStateWebsocketHandler(Plugin):
|
||||
message_id, num_messages=payload.num_messages
|
||||
)
|
||||
await self.signal_operation_done(allow_auto_save=False)
|
||||
|
||||
@set_loading("Advancing time", cancellable=True, as_async=True)
|
||||
async def handle_advance_time(self, data: dict):
|
||||
payload = AdvanceTimePayload(**data)
|
||||
await self.agent.advance_time(payload.iso_duration)
|
||||
await self.signal_operation_done()
|
||||
|
||||
@@ -165,20 +165,7 @@
|
||||
|
||||
<!-- advance time -->
|
||||
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn class="hotkey mx-1" v-bind="props" :disabled="appBusy || !appReady" color="primary" icon variant="text">
|
||||
<v-icon>mdi-clock</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-subheader>Advance Time</v-list-subheader>
|
||||
<v-list-item density="compact" v-for="(option, index) in advanceTimeOptions" :key="index"
|
||||
@click="advanceTime(option.value)">
|
||||
<v-list-item-title density="compact" class="text-capitalize">{{ option.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<SceneToolsTime :disabled="appBusy || !appReady" />
|
||||
|
||||
<!-- world tools -->
|
||||
<SceneToolsWorld
|
||||
@@ -234,6 +221,7 @@ import SceneToolsVisual from './SceneToolsVisual.vue';
|
||||
import SceneToolsWorld from './SceneToolsWorld.vue';
|
||||
import SceneToolsSettings from './SceneToolsSettings.vue';
|
||||
import SceneToolsSave from './SceneToolsSave.vue';
|
||||
import SceneToolsTime from './SceneToolsTime.vue';
|
||||
import RequestInput from './RequestInput.vue';
|
||||
export default {
|
||||
|
||||
@@ -247,6 +235,7 @@ export default {
|
||||
SceneToolsSave,
|
||||
SceneToolsWorld,
|
||||
SceneToolsSettings,
|
||||
SceneToolsTime,
|
||||
RequestInput,
|
||||
},
|
||||
props: {
|
||||
@@ -312,29 +301,6 @@ export default {
|
||||
{"value": "toggleAutoSave", "title": "Auto Save", "icon": "mdi-content-save", "description": "Automatically save after each game-loop", "status": () => { return this.canAutoSave ? this.autoSave : "Manually save scene for auto-save to be available"; }},
|
||||
{"value": "toggleAutoProgress", "title": "Auto Progress", "icon": "mdi-robot", "description": "AI automatically progresses after player turn.", "status": () => { return this.autoProgress }},
|
||||
],
|
||||
advanceTimeOptions: [
|
||||
{"value" : "P10Y", "title": "10 years"},
|
||||
{"value" : "P5Y", "title": "5 years"},
|
||||
{"value" : "P3Y", "title": "3 years"},
|
||||
{"value" : "P2Y", "title": "2 years"},
|
||||
{"value" : "P1Y", "title": "1 year"},
|
||||
{"value" : "P6M", "title": "6 months"},
|
||||
{"value" : "P3M", "title": "3 months"},
|
||||
{"value" : "P1M", "title": "1 month"},
|
||||
{"value" : "P14D:2 Weeks later", "title": "2 weeks"},
|
||||
{"value" : "P7D:1 Week later", "title": "1 week"},
|
||||
{"value" : "P3D", "title": "3 days"},
|
||||
{"value" : "P2D", "title": "2 days"},
|
||||
{"value" : "P1D", "title": "1 day"},
|
||||
{"value" : "PT12H", "title": "12 hours"},
|
||||
{"value" : "PT8H", "title": "8 hours"},
|
||||
{"value" : "PT4H", "title": "4 hours"},
|
||||
{"value" : "PT2H", "title": "2 hours"},
|
||||
{"value" : "PT1H", "title": "1 hour"},
|
||||
{"value" : "PT30M", "title": "30 minutes"},
|
||||
{"value" : "PT15M", "title": "15 minutes"},
|
||||
{"value" : "PT5M", "title": "5 minutes"}
|
||||
],
|
||||
}
|
||||
},
|
||||
inject: [
|
||||
@@ -460,10 +426,6 @@ export default {
|
||||
this.$emit('cancel-audio-queue');
|
||||
},
|
||||
|
||||
advanceTime(duration) {
|
||||
this.getWebsocket().send(JSON.stringify({ type: 'world_state_agent', action: 'advance_time', duration: duration }));
|
||||
},
|
||||
|
||||
// Handle incoming messages
|
||||
|
||||
handleMessage(data) {
|
||||
|
||||
153
talemate_frontend/src/components/SceneToolsTime.vue
Normal file
153
talemate_frontend/src/components/SceneToolsTime.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn class="hotkey mx-1" v-bind="props" :disabled="disabled" color="primary" icon variant="text">
|
||||
<v-icon>mdi-clock</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-subheader>Advance Time</v-list-subheader>
|
||||
|
||||
<v-menu v-for="group in timeGroups" :key="group.label" open-on-hover location="end">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-list-item v-bind="props" @click.stop :prepend-icon="group.icon" append-icon="mdi-chevron-right" density="compact">
|
||||
<v-list-item-title>{{ group.label }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item v-for="option in group.options" :key="option.value"
|
||||
density="compact" @click="advanceTime(option.value)">
|
||||
<v-list-item-title class="text-capitalize">{{ option.title }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<v-divider />
|
||||
<v-list-item density="compact" @click="openCustomTimeDialog">
|
||||
<v-list-item-title>Custom...</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<!-- Custom Time Dialog -->
|
||||
<v-dialog v-model="customTimeDialog" max-width="400">
|
||||
<v-card>
|
||||
<v-card-title class="text-body-1">Advance Time</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="d-flex align-center">
|
||||
<v-number-input v-model="customTimeAmount" :min="1" label="Amount"
|
||||
style="max-width: 180px" hide-details="auto" />
|
||||
<v-select v-model="customTimeUnit" :items="customTimeUnits" label="Unit"
|
||||
style="max-width: 180px" hide-details="auto" class="ml-2" />
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="text" @click="customTimeDialog = false">Cancel</v-btn>
|
||||
<v-btn color="primary" @click="submitCustomTime">Advance</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SceneToolsTime",
|
||||
props: {
|
||||
disabled: Boolean,
|
||||
},
|
||||
inject: ['getWebsocket'],
|
||||
data() {
|
||||
return {
|
||||
customTimeDialog: false,
|
||||
customTimeAmount: 1,
|
||||
customTimeUnit: 'hours',
|
||||
customTimeUnits: ['minutes', 'hours', 'days', 'weeks', 'months', 'years'],
|
||||
timeGroups: [
|
||||
{
|
||||
label: "Minutes",
|
||||
icon: "mdi-timer-sand",
|
||||
options: [
|
||||
{"value": "PT5M", "title": "5 minutes"},
|
||||
{"value": "PT15M", "title": "15 minutes"},
|
||||
{"value": "PT30M", "title": "30 minutes"},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Hours",
|
||||
icon: "mdi-clock-outline",
|
||||
options: [
|
||||
{"value": "PT1H", "title": "1 hour"},
|
||||
{"value": "PT2H", "title": "2 hours"},
|
||||
{"value": "PT4H", "title": "4 hours"},
|
||||
{"value": "PT8H", "title": "8 hours"},
|
||||
{"value": "PT12H", "title": "12 hours"},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Days",
|
||||
icon: "mdi-weather-sunny",
|
||||
options: [
|
||||
{"value": "P1D", "title": "1 day"},
|
||||
{"value": "P2D", "title": "2 days"},
|
||||
{"value": "P3D", "title": "3 days"},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Weeks",
|
||||
icon: "mdi-calendar-week",
|
||||
options: [
|
||||
{"value": "P7D", "title": "1 week"},
|
||||
{"value": "P14D", "title": "2 weeks"},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Months",
|
||||
icon: "mdi-calendar-month",
|
||||
options: [
|
||||
{"value": "P1M", "title": "1 month"},
|
||||
{"value": "P3M", "title": "3 months"},
|
||||
{"value": "P6M", "title": "6 months"},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Years",
|
||||
icon: "mdi-calendar-multiple",
|
||||
options: [
|
||||
{"value": "P1Y", "title": "1 year"},
|
||||
{"value": "P2Y", "title": "2 years"},
|
||||
{"value": "P3Y", "title": "3 years"},
|
||||
{"value": "P5Y", "title": "5 years"},
|
||||
{"value": "P10Y", "title": "10 years"},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
advanceTime(duration) {
|
||||
this.getWebsocket().send(JSON.stringify({
|
||||
type: 'world_state_agent',
|
||||
action: 'advance_time',
|
||||
duration: duration,
|
||||
}));
|
||||
},
|
||||
|
||||
openCustomTimeDialog() {
|
||||
this.customTimeAmount = 1;
|
||||
this.customTimeUnit = 'hours';
|
||||
this.customTimeDialog = true;
|
||||
},
|
||||
|
||||
submitCustomTime() {
|
||||
this.getWebsocket().send(JSON.stringify({
|
||||
type: 'world_state_agent',
|
||||
action: 'advance_time',
|
||||
amount: this.customTimeAmount,
|
||||
unit: this.customTimeUnit,
|
||||
}));
|
||||
this.customTimeDialog = false;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user