Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
de42cb7
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 5, 2026
3cfd09d
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 5, 2026
1f66a51
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
a260929
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
ec9623a
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
19d1200
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
84a6f0f
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
52f8b3d
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
59f66bf
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
2769e81
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
7aec98d
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
39a1e98
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
1395e00
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
54478fe
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
16dac02
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
a5e667b
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
24aa502
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
98012b7
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
53a4462
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
1eef9b5
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
50b144d
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
d88f1ec
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
a262182
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
83b256a
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
0349b85
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
e219be1
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
e1f990c
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Content-Length
google-labs-jules[bot] Feb 6, 2026
f7c85cd
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Stream Size
google-labs-jules[bot] Feb 7, 2026
517963d
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Stream Size
google-labs-jules[bot] Feb 7, 2026
ca97b51
Fix cURL Error 26 (SSE Size Mismatch) by enforcing Stream Size
google-labs-jules[bot] Feb 7, 2026
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
1 change: 1 addition & 0 deletions lib/BackgroundJobs/IndexerJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ protected function index(array $files): void {
$file->getMtime(),
$file->getMimeType(),
ProviderConfigService::getDefaultProviderKey(),
(int)$fileSize,
);
$allSourceIds[] = ProviderConfigService::getSourceId($file->getId());

Expand Down
22 changes: 20 additions & 2 deletions lib/Service/LangRopeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private function requestToExApp(

// todo: app_api is always available now (composer update)
try {
$appApiFunctions = \OCP\Server::get(\OCA\AppAPI\PublicFunctions::class);
$appApiFunctions = $this->getAppApiFunctions();
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
throw new RuntimeException('Could not get AppAPI public functions');
}
Expand Down Expand Up @@ -287,10 +287,24 @@ public function indexSources(array $sources): array {
}

$params = array_map(function (Source $source) {
$contents = $source->content;
if ($source->size !== null) {
if (class_exists('\GuzzleHttp\Psr7\Utils')) {
$stream = \GuzzleHttp\Psr7\Utils::streamFor($source->content);
} else {
$stream = \GuzzleHttp\Psr7\stream_for($source->content);
}
$contents = \GuzzleHttp\Psr7\FnStream::decorate($stream, [
'getSize' => function () use ($source) {
return $source->size;
},
]);
}

return [
'name' => 'sources',
'filename' => $source->reference, // eg. 'files__default: 555'
'contents' => $source->content,
'contents' => $contents,
'headers' => [
'userIds' => implode(',', $source->userIds),
'title' => $source->title,
Expand Down Expand Up @@ -424,4 +438,8 @@ public function getWithPresentableSources(string $llmResponse, string ...$source

return $llmResponse . $output;
}

protected function getAppApiFunctions() {
return \OCP\Server::get(\OCA\AppAPI\PublicFunctions::class);
}
}
10 changes: 10 additions & 0 deletions lib/Service/ScanService.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ public function scanUserFiles(string $userId, array $mimeTypeFilter, ?string $di
$userFolder = $this->root->getUserFolder($userId)->get($directory);
}

if ($userFolder instanceof File) {
$source = $this->getSourceFromFile($mimeTypeFilter, $userFolder);
if ($source !== null) {
$this->langRopeService->indexSources([$source]);
yield $source;
}
return [];
}

yield from ($this->scanDirectory($mimeTypeFilter, $userFolder));
return [];
}
Expand Down Expand Up @@ -123,6 +132,7 @@ public function getSourceFromFile(array $mimeTypeFilter, File $node): ?Source {
$node->getMTime(),
$node->getMimeType(),
$providerKey,
(int)$node->getSize(),
);
}
}
1 change: 1 addition & 0 deletions lib/Type/Source.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public function __construct(
public int|string $modified,
public string $type,
public string $provider,
public ?int $size = null,
) {
}
}
11 changes: 11 additions & 0 deletions mock_server.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* Serving Flask app 'mock_server'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:23000
* Running on http://192.168.0.2:23000
Press CTRL+C to quit
127.0.0.1 - - [05/Feb/2026 19:24:49] "PUT /loadSources HTTP/1.1" 200 -
127.0.0.1 - - [05/Feb/2026 19:31:28] "PUT /loadSources HTTP/1.1" 400 -
127.0.0.1 - - [05/Feb/2026 19:32:18] "PUT /loadSources HTTP/1.1" 200 -
127.0.0.1 - - [05/Feb/2026 19:32:20] "PUT /loadSources HTTP/1.1" 400 -
26 changes: 26 additions & 0 deletions tests/reproduction/create_test_file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
define('NC_CLI_MODE', true);
require_once '/var/www/html/lib/base.php';

use OCP\Server;
use OCP\Files\IRootFolder;

try {
$rootFolder = Server::get(IRootFolder::class);
$userFolder = $rootFolder->getUserFolder('admin');

if ($userFolder->nodeExists('test.txt')) {
$file = $userFolder->get('test.txt');
$file->delete();
}

$file = $userFolder->newFile('test.txt');
// Write 1MB of data
$file->putContent(str_repeat('A', 1024 * 1024));

echo "Created encrypted test.txt successfully.\n";

} catch (\Exception $e) {
echo "Error creating file: " . $e->getMessage() . "\n";
exit(1);
}
36 changes: 36 additions & 0 deletions tests/reproduction/debug_sizes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
define('NC_CLI_MODE', true);
require_once '/var/www/html/lib/base.php';

use OCP\Server;
use OCP\Files\IRootFolder;

function checkFile($path) {
try {
$rootFolder = Server::get(IRootFolder::class);
$userFolder = $rootFolder->getUserFolder('admin');
if (!$userFolder->nodeExists($path)) {
echo "File $path not found.\n";
return;
}
$file = $userFolder->get($path);

$reportedSize = $file->getSize();
echo "File::getSize() for $path: " . $reportedSize . "\n";

$handle = $file->fopen('rb');
$stat = fstat($handle);
echo "fstat()['size'] for $path: " . $stat['size'] . "\n";

$contents = stream_get_contents($handle);
$actualReadSize = strlen($contents);
echo "Actual Read Size for $path: " . $actualReadSize . "\n";

echo "Mismatch for $path: " . ($reportedSize - $actualReadSize) . "\n";
} catch (\Exception $e) {
echo "Error checking $path: " . $e->getMessage() . "\n";
}
}

checkFile('test.txt');
checkFile('Nextcloud Manual.pdf'); // Check the default file too
20 changes: 20 additions & 0 deletions tests/reproduction/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
services:
nextcloud:
image: nextcloud:latest
environment:
- NEXTCLOUD_ADMIN_USER=admin
- NEXTCLOUD_ADMIN_PASSWORD=password
volumes:
- ../../:/var/www/html/custom_apps/context_chat
ports:
- "8080:80"

context_chat_backend:
image: python:3.9-slim
command: sh -c "pip install flask && python /mock_server.py"
volumes:
- ./mock_server.py:/mock_server.py
networks:
default:
aliases:
- context_chat_backend
33 changes: 33 additions & 0 deletions tests/reproduction/mock_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from flask import Flask, request, jsonify
import sys

app = Flask(__name__)

@app.route('/heartbeat', methods=['GET'])
def heartbeat():
return jsonify({"status": "ok"}), 200

@app.route('/loadSources', methods=['PUT'])
def load_sources():
content_length = request.headers.get('Content-Length')
if content_length:
content_length = int(content_length)

body = request.get_data()
actual_length = len(body)

print(f"Header Content-Length: {content_length}")
print(f"Actual Body Length: {actual_length}")

if content_length is not None and content_length != actual_length:
print("FAIL: Size Mismatch")
return jsonify({"error": "Size Mismatch"}), 400

print("SUCCESS")
return jsonify({
"loaded_sources": ["test_source"],
"sources_to_retry": []
}), 200

if __name__ == '__main__':
app.run(host='0.0.0.0', port=23000)
52 changes: 52 additions & 0 deletions tests/reproduction/register_mock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
define('NC_CLI_MODE', true);
require_once '/var/www/html/console.php';

use OCP\Server;
use OCA\AppAPI\Db\ExApp;
use OCA\AppAPI\Db\ExAppMapper;

try {
if (!class_exists(ExAppMapper::class)) {
echo "AppAPI classes not found.\n";
exit(1);
}

$mapper = Server::get(ExAppMapper::class);

// Try to find existing
try {
$existing = $mapper->find('context_chat_backend');
$mapper->delete($existing);
echo "Deleted existing registration.\n";
} catch (\Exception $e) {
// Not found, ignore
}

$exApp = new ExApp();
$exApp->setAppId('context_chat_backend');
$exApp->setName('Context Chat Backend');
$exApp->setDeployMethod('manual_install');
$exApp->setVersion('1.0.0');
$exApp->setEnabled(1);
$exApp->setHost('context_chat_backend');
$exApp->setPort(23000);
$exApp->setProtocol('http');
$exApp->setSecret('secret');
$exApp->setHash('hash');
$exApp->setLastUpdated(time());

// Set other required fields if any (based on standard ExApp entity)
// Some versions require 'scopes' or 'daemon_config_name'
if (method_exists($exApp, 'setDaemonConfigName')) {
$exApp->setDaemonConfigName('manual_install');
}

$mapper->insert($exApp);

echo "Registered context_chat_backend successfully via Mapper.\n";

} catch (\Exception $e) {
echo "Error registering app: " . $e->getMessage() . "\n";
exit(1);
}
104 changes: 104 additions & 0 deletions tests/reproduction/run_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/bin/bash
set -e

# Start containers
docker-compose up -d

echo "Waiting for container to accept commands..."
sleep 10

# Check if Nextcloud is installed
echo "Checking Nextcloud status..."
if docker-compose exec -u 33 nextcloud php occ status | grep -q "installed: true"; then
echo "Nextcloud is already installed."
else
echo "Nextcloud is not installed. Installing..."
docker-compose exec -u 33 nextcloud php occ maintenance:install \
--database "sqlite" \
--admin-user "admin" \
--admin-pass "password"
fi

echo "Waiting for Nextcloud to be fully ready..."
max_retries=10
count=0
while [ $count -lt $max_retries ]; do
if docker-compose exec -u 33 nextcloud php occ status | grep -q "installed: true"; then
echo "Nextcloud is ready."
break
fi
echo "Waiting for status update... (Attempt $((count+1))/$max_retries)"
sleep 5
count=$((count+1))
done

if [ $count -eq $max_retries ]; then
echo "Timeout waiting for Nextcloud to be ready."
exit 1
fi

echo "Configuring Nextcloud..."

# Enable encryption
docker-compose exec -u 33 nextcloud php occ app:enable encryption
docker-compose exec -u 33 nextcloud php occ encryption:enable
docker-compose exec -u 33 nextcloud php occ encryption:enable-master-key

# Enable apps
docker-compose exec -u 33 nextcloud php occ app:enable context_chat
docker-compose exec -u 33 nextcloud php occ app:enable app_api

# Register Mock Backend via OCC
echo "Cleaning up previous registrations..."
docker-compose exec -u 33 nextcloud php occ app_api:app:unregister context_chat_backend --force --no-interaction || true
docker-compose exec -u 33 nextcloud php occ app_api:daemon:unregister manual_install --no-interaction || true

echo "Registering Mock Backend..."

# Register daemon config
# Using just hostname for daemon, allowing app port to be appended correctly
docker-compose exec -u 33 nextcloud php occ app_api:daemon:register manual_install "Manual Install" manual-install http context_chat_backend http://localhost --no-interaction || true

# Register the app
# We use --force-scopes to avoid interactive prompts
docker-compose exec -u 33 nextcloud php occ app_api:app:register context_chat_backend manual_install --json-info '{"id":"context_chat_backend","name":"Context Chat Backend","deploy_method":"manual_install","version":"1.0.0","secret":"secret","host":"context_chat_backend","port":23000,"scopes":[],"protocol":"http","system_app":0}' --force-scopes --no-interaction || true

# Enable the app (it was listed as disabled)
echo "Enabling Context Chat Backend..."
docker-compose exec -u 33 nextcloud php occ app_api:app:enable context_chat_backend --no-interaction || true

# Debug: List registered apps
echo "Listing AppAPI apps..."
docker-compose exec -u 33 nextcloud php occ app_api:app:list

# Configure context_chat
docker-compose exec -u 33 nextcloud php occ config:app:set context_chat backend_init --value true

# Create test file
echo "Creating test file via VFS (Encrypted)..."
docker-compose cp create_test_file.php nextcloud:/var/www/html/create_test_file.php
docker-compose exec -u 33 nextcloud php /var/www/html/create_test_file.php

# Verify file existence via PHP
echo "Verifying file existence in Nextcloud VFS..."
if docker-compose exec -u 33 nextcloud php -r 'define("NC_CLI_MODE", true); require_once "/var/www/html/lib/base.php"; echo \OCP\Server::get(\OCP\Files\IRootFolder::class)->getUserFolder("admin")->nodeExists("test.txt") ? "YES" : "NO";' | grep -q "YES"; then
echo "SUCCESS: test.txt found in Nextcloud VFS."
else
echo "FAILURE: test.txt NOT found in Nextcloud VFS."
exit 1
fi

# DEBUG: Check Sizes
echo "DEBUG: Checking file sizes..."
docker-compose cp debug_sizes.php nextcloud:/var/www/html/debug_sizes.php
docker-compose exec -u 33 nextcloud php /var/www/html/debug_sizes.php

# Run Indexer on the specific file
echo "Running Scan (Direct Indexing) on test.txt..."
docker-compose exec -u 33 nextcloud php occ context_chat:scan admin --directory test.txt

# Check logs
echo "Checking backend logs..."
docker-compose logs --no-log-prefix context_chat_backend

echo "Test completed successfully."
Loading