From c210049602251b1f3e8e2125f7bd89f08b4d0e46 Mon Sep 17 00:00:00 2001 From: Arkadiusz Sitkiewicz Date: Mon, 10 Aug 2026 13:59:39 +0200 Subject: [PATCH 1/4] feat: add configurable summary system prompt Assisted-by: ChatGPT:GPT-5.6 Luna Signed-off-by: Arkadiusz Sitkiewicz --- lib/AppInfo/Application.php | 3 +++ lib/Service/OpenAiSettingsService.php | 7 +++++++ lib/TaskProcessing/SummaryProvider.php | 3 +-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 27be6312..0781286b 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -64,6 +64,9 @@ class Application extends App implements IBootstrap { public const DEFAULT_QUOTA_PERIOD = 30; public const DEFAULT_QUOTA_CONFIG = ['length' => self::DEFAULT_QUOTA_PERIOD, 'unit' => 'day', 'day' => 1]; + public const DEFAULT_SUMMARY_SYSTEM_PROMPT = 'You are a helpful assistant that summarizes text in the same language as the text. ' + . 'You should only return the summary without any additional information. '; + public const DEFAULT_OPENAI_TEXT_GENERATION_TIME = 10; // seconds public const DEFAULT_LOCALAI_TEXT_GENERATION_TIME = 60; // seconds public const DEFAULT_OPENAI_IMAGE_GENERATION_TIME = 20; // seconds diff --git a/lib/Service/OpenAiSettingsService.php b/lib/Service/OpenAiSettingsService.php index f63705c1..7974c446 100644 --- a/lib/Service/OpenAiSettingsService.php +++ b/lib/Service/OpenAiSettingsService.php @@ -318,6 +318,13 @@ public function getQuotaPeriod(): array { return $value; } + /** + * @return string + */ + public function getSummarySystemPrompt(): string { + return $this->appConfig->getValueString(Application::APP_ID, 'system_prompt_summary', Application::DEFAULT_SUMMARY_SYSTEM_PROMPT, lazy: true) ?: Application::DEFAULT_SUMMARY_SYSTEM_PROMPT; + } + /** * @return int[] */ diff --git a/lib/TaskProcessing/SummaryProvider.php b/lib/TaskProcessing/SummaryProvider.php index ab9dce24..55fa4c79 100644 --- a/lib/TaskProcessing/SummaryProvider.php +++ b/lib/TaskProcessing/SummaryProvider.php @@ -153,8 +153,7 @@ public function process(?string $userId, array $input, callable $reportProgress) try { $completions = []; - $summarySystemPrompt = 'You are a helpful assistant that summarizes text in the same language as the text. ' - . 'You should only return the summary without any additional information. '; + $summarySystemPrompt = $this->openAiSettingsService->getSummarySystemPrompt(); if (isset($input['format'])) { if ($input['format'] === 'paragraph') { $summarySystemPrompt .= 'Return the summary as a paragraph. '; From 5b0091e029bed1eb86f35b87d508fec2bb798d6f Mon Sep 17 00:00:00 2001 From: Arkadiusz Sitkiewicz Date: Mon, 10 Aug 2026 14:00:43 +0200 Subject: [PATCH 2/4] test: add tests for configurable summary system prompt Assisted-by: ChatGPT:GPT-5.6 Luna Signed-off-by: Arkadiusz Sitkiewicz --- tests/unit/Providers/OpenAiProviderTest.php | 82 +++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tests/unit/Providers/OpenAiProviderTest.php b/tests/unit/Providers/OpenAiProviderTest.php index d739eb65..93b8cb43 100644 --- a/tests/unit/Providers/OpenAiProviderTest.php +++ b/tests/unit/Providers/OpenAiProviderTest.php @@ -574,6 +574,88 @@ public function testSummaryProvider(): void { $this->quotaUsageMapper->deleteUserQuotaUsages(self::TEST_USER1); } + public function testSummaryProviderConfigurableSystemPrompt(): void { + try { + $configurableSystemPrompt = 'Configurable summary system prompt from app config'; + $appConfig = \OCP\Server::get(IAppConfig::class); + $appConfig->setValueString( + Application::APP_ID, + 'system_prompt_summary', + $configurableSystemPrompt, + ); + + $summaryProvider = new SummaryProvider( + $this->openAiApiService, + $this->openAiSettingsService, + $this->createMock(\OCP\IL10N::class), + $this->chunkService, + self::TEST_USER1, + ); + + $prompt = 'This is a test prompt'; + $n = 1; + + $response = '{ + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1677652288, + "model": "gpt-3.5-turbo-0613", + "system_fingerprint": "fp_44709d6fcb", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "This is a test response." + }, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 9, + "completion_tokens": 12, + "total_tokens": 21 + } + }'; + + $url = self::OPENAI_API_BASE . 'chat/completions'; + + $options = ['timeout' => Application::OPENAI_DEFAULT_REQUEST_TIMEOUT, 'headers' => ['User-Agent' => Application::USER_AGENT, 'Authorization' => self::AUTHORIZATION_HEADER, 'Content-Type' => 'application/json']]; + $options['body'] = json_encode([ + 'model' => Application::DEFAULT_COMPLETION_MODEL_ID, + 'messages' => [ + ['role' => 'system', 'content' => $configurableSystemPrompt], + ['role' => 'user', 'content' => $prompt], + ], + 'n' => $n, + 'stream' => false, + 'max_completion_tokens' => Application::DEFAULT_MAX_NUM_OF_TOKENS, + 'user' => self::TEST_USER1, + ]); + + $iResponse = $this->createMock(\OCP\Http\Client\IResponse::class); + $iResponse->method('getBody')->willReturn($response); + $iResponse->method('getStatusCode')->willReturn(200); + $iResponse->method('getHeader')->with('Content-Type')->willReturn('application/json'); + + $this->iClient->expects($this->once())->method('post')->with($url, $options)->willReturn($iResponse); + + $result = $summaryProvider->process(self::TEST_USER1, ['input' => $prompt], fn () => true); + $this->assertEquals('This is a test response.', $result['output']); + + // Check that token usage is logged properly + $usage = $this->quotaUsageMapper->getQuotaUnitsOfUser(self::TEST_USER1, Application::QUOTA_TYPE_TEXT); + $this->assertEquals(21, $usage); + // Clear quota usage + $this->quotaUsageMapper->deleteUserQuotaUsages(self::TEST_USER1); + } finally { + $appConfig->deleteKey( + Application::APP_ID, + 'system_prompt_summary' + ); + } + } + public function testProofreadProvider(): void { $proofreadProvider = new ProofreadProvider( $this->openAiApiService, From 81e1c3f11847c537e577e8578bab6ce99baea392 Mon Sep 17 00:00:00 2001 From: Arkadiusz Sitkiewicz Date: Mon, 10 Aug 2026 15:25:56 +0200 Subject: [PATCH 3/4] feat: add configurable talk summary system prompt Assisted-by: ChatGPT:GPT-5.6 Luna Signed-off-by: Arkadiusz Sitkiewicz --- lib/Service/OpenAiSettingsService.php | 7 +++++++ lib/TaskProcessing/SummaryProvider.php | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Service/OpenAiSettingsService.php b/lib/Service/OpenAiSettingsService.php index 7974c446..d4226afd 100644 --- a/lib/Service/OpenAiSettingsService.php +++ b/lib/Service/OpenAiSettingsService.php @@ -325,6 +325,13 @@ public function getSummarySystemPrompt(): string { return $this->appConfig->getValueString(Application::APP_ID, 'system_prompt_summary', Application::DEFAULT_SUMMARY_SYSTEM_PROMPT, lazy: true) ?: Application::DEFAULT_SUMMARY_SYSTEM_PROMPT; } + /** + * @return string + */ + public function getTalkSummarySystemPrompt(): string { + return $this->appConfig->getValueString(Application::APP_ID, 'system_prompt_talk_summary', Application::DEFAULT_SUMMARY_SYSTEM_PROMPT, lazy: true) ?: Application::DEFAULT_SUMMARY_SYSTEM_PROMPT; + } + /** * @return int[] */ diff --git a/lib/TaskProcessing/SummaryProvider.php b/lib/TaskProcessing/SummaryProvider.php index 55fa4c79..b74720b1 100644 --- a/lib/TaskProcessing/SummaryProvider.php +++ b/lib/TaskProcessing/SummaryProvider.php @@ -79,6 +79,11 @@ public function getOptionalInputShape(): array { $this->l->t('The model used to generate the completion'), EShapeType::Enum ), + 'talk_meeting' => new ShapeDescriptor( + $this->l->t("Talk meeting flag"), + $this->l->t('Flag that indicates if the summary is generated from a Talk meeting recording.'), + EShapeType::Number + ), ]; } @@ -106,6 +111,7 @@ public function getOptionalInputShapeDefaults(): array { 'model' => $adminModel, 'format' => 'auto', 'complexity' => 'medium', + 'talk_meeting' => 0, ]; } @@ -139,6 +145,7 @@ public function process(?string $userId, array $input, callable $reportProgress) $model = $input['model']; } + $isTalkMeeting = (bool)($input['talk_meeting'] ?? 0); $prompts = $this->chunkService->chunkSplitPrompt($prompt); $newNumChunks = count($prompts); $progress = 0.0; @@ -153,7 +160,11 @@ public function process(?string $userId, array $input, callable $reportProgress) try { $completions = []; - $summarySystemPrompt = $this->openAiSettingsService->getSummarySystemPrompt(); + if ($isTalkMeeting) { + $summarySystemPrompt = $this->openAiSettingsService->getTalkSummarySystemPrompt(); + } else { + $summarySystemPrompt = $this->openAiSettingsService->getSummarySystemPrompt(); + } if (isset($input['format'])) { if ($input['format'] === 'paragraph') { $summarySystemPrompt .= 'Return the summary as a paragraph. '; From b483820957d88e9bc065ec3d494fa8d731cbcf2a Mon Sep 17 00:00:00 2001 From: Arkadiusz Sitkiewicz Date: Mon, 10 Aug 2026 15:26:34 +0200 Subject: [PATCH 4/4] test: add tests for configurable talk summary system prompt Assisted-by: ChatGPT:GPT-5.6 Luna Signed-off-by: Arkadiusz Sitkiewicz --- tests/unit/Providers/OpenAiProviderTest.php | 82 +++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tests/unit/Providers/OpenAiProviderTest.php b/tests/unit/Providers/OpenAiProviderTest.php index 93b8cb43..a314cbcc 100644 --- a/tests/unit/Providers/OpenAiProviderTest.php +++ b/tests/unit/Providers/OpenAiProviderTest.php @@ -656,6 +656,88 @@ public function testSummaryProviderConfigurableSystemPrompt(): void { } } + public function testSummaryProviderConfigurableTalkSystemPrompt(): void { + try { + $configurableSystemPrompt = 'Configurable Talk summary system prompt from app config'; + $appConfig = \OCP\Server::get(IAppConfig::class); + $appConfig->setValueString( + Application::APP_ID, + 'system_prompt_talk_summary', + $configurableSystemPrompt, + ); + + $summaryProvider = new SummaryProvider( + $this->openAiApiService, + $this->openAiSettingsService, + $this->createMock(\OCP\IL10N::class), + $this->chunkService, + self::TEST_USER1, + ); + + $prompt = 'This is a test prompt'; + $n = 1; + + $response = '{ + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1677652288, + "model": "gpt-3.5-turbo-0613", + "system_fingerprint": "fp_44709d6fcb", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "This is a test response." + }, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 9, + "completion_tokens": 12, + "total_tokens": 21 + } + }'; + + $url = self::OPENAI_API_BASE . 'chat/completions'; + + $options = ['timeout' => Application::OPENAI_DEFAULT_REQUEST_TIMEOUT, 'headers' => ['User-Agent' => Application::USER_AGENT, 'Authorization' => self::AUTHORIZATION_HEADER, 'Content-Type' => 'application/json']]; + $options['body'] = json_encode([ + 'model' => Application::DEFAULT_COMPLETION_MODEL_ID, + 'messages' => [ + ['role' => 'system', 'content' => $configurableSystemPrompt], + ['role' => 'user', 'content' => $prompt], + ], + 'n' => $n, + 'stream' => false, + 'max_completion_tokens' => Application::DEFAULT_MAX_NUM_OF_TOKENS, + 'user' => self::TEST_USER1, + ]); + + $iResponse = $this->createMock(\OCP\Http\Client\IResponse::class); + $iResponse->method('getBody')->willReturn($response); + $iResponse->method('getStatusCode')->willReturn(200); + $iResponse->method('getHeader')->with('Content-Type')->willReturn('application/json'); + + $this->iClient->expects($this->once())->method('post')->with($url, $options)->willReturn($iResponse); + + $result = $summaryProvider->process(self::TEST_USER1, ['input' => $prompt, 'talk_meeting' => 1], fn () => true); + $this->assertEquals('This is a test response.', $result['output']); + + // Check that token usage is logged properly + $usage = $this->quotaUsageMapper->getQuotaUnitsOfUser(self::TEST_USER1, Application::QUOTA_TYPE_TEXT); + $this->assertEquals(21, $usage); + // Clear quota usage + $this->quotaUsageMapper->deleteUserQuotaUsages(self::TEST_USER1); + } finally { + $appConfig->deleteKey( + Application::APP_ID, + 'system_prompt_talk_summary' + ); + } + } + public function testProofreadProvider(): void { $proofreadProvider = new ProofreadProvider( $this->openAiApiService,