Skip to content

Commit 68d5962

Browse files
petuch03sproshev
andauthored
Reworked FileSystemProvider (#557)
Co-authored-by: Semyon Proshev <[email protected]>
1 parent 2877f4a commit 68d5962

File tree

14 files changed

+798
-196
lines changed

14 files changed

+798
-196
lines changed

agents/agents-features/agents-features-memory/src/commonMain/kotlin/ai/koog/agents/memory/providers/LocalFileMemoryProvider.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public data class LocalFileMemoryProvider<Path>(
133133
is MemoryScope.Product -> listOf("product", scope.name, "subject", subject.name)
134134
MemoryScope.CrossProduct -> listOf("organization", "subject", subject.name)
135135
}
136-
return segments.fold(root) { acc, segment -> fs.fromRelativeString(acc, segment) }
136+
return segments.fold(root) { acc, segment -> fs.joinPath(acc, segment) }
137137
}
138138

139139
/**
@@ -202,7 +202,7 @@ public data class LocalFileMemoryProvider<Path>(
202202
*/
203203
override suspend fun save(fact: Fact, subject: MemorySubject, scope: MemoryScope) {
204204
val path = getStoragePath(subject, scope)
205-
storage.createDirectories(fs.fromRelativeString(root, config.storageDirectory))
205+
storage.createDirectories(fs.joinPath(root, config.storageDirectory))
206206

207207
val facts = loadFacts(path).toMutableMap()
208208
val key = fact.concept.keyword

agents/agents-features/agents-features-memory/src/commonMain/kotlin/ai/koog/agents/memory/storage/Storage.kt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package ai.koog.agents.memory.storage
22

3-
import ai.koog.rag.base.files.FileMetadata
43
import ai.koog.rag.base.files.FileSystemProvider
4+
import ai.koog.rag.base.files.createDirectory
5+
import ai.koog.rag.base.files.createFile
6+
import ai.koog.rag.base.files.readText
7+
import ai.koog.rag.base.files.writeText
58

69
/**
710
* Platform-independent encryption abstraction for secure data storage.
@@ -181,7 +184,7 @@ public open class SimpleStorage<Path>(
181184
*/
182185
override suspend fun read(path: Path): String? {
183186
if (!fs.exists(path)) return null
184-
return fs.read(path).decodeToString()
187+
return fs.readText(path)
185188
}
186189

187190
/**
@@ -201,9 +204,9 @@ public open class SimpleStorage<Path>(
201204
val name = fs.name(path)
202205

203206
if (!fs.exists(path)) {
204-
fs.create(parent, name, FileMetadata.FileType.File)
207+
fs.createFile(fs.joinPath(parent, name))
205208
}
206-
fs.write(path, content.encodeToByteArray())
209+
fs.writeText(path, content)
207210
}
208211

209212
/**
@@ -220,7 +223,7 @@ public open class SimpleStorage<Path>(
220223
if (!fs.exists(path)) {
221224
val parent = fs.parent(path) ?: return
222225
val name = fs.name(path)
223-
fs.create(parent, name, FileMetadata.FileType.Directory)
226+
fs.createDirectory(fs.joinPath(parent, name))
224227
}
225228
}
226229
}
@@ -282,7 +285,7 @@ public class EncryptedStorage<Path>(
282285
*/
283286
override suspend fun read(path: Path): String? {
284287
if (!fs.exists(path)) return null
285-
val content = fs.read(path).decodeToString()
288+
val content = fs.readText(path)
286289
return encryption.decrypt(content)
287290
}
288291

@@ -305,9 +308,9 @@ public class EncryptedStorage<Path>(
305308

306309
val encrypted = encryption.encrypt(content)
307310
if (!fs.exists(path)) {
308-
fs.create(parent, name, FileMetadata.FileType.File)
311+
fs.createFile(fs.joinPath(parent, name))
309312
}
310-
fs.write(path, encrypted.encodeToByteArray())
313+
fs.writeText(path, encrypted)
311314
}
312315

313316
/**
@@ -323,7 +326,7 @@ public class EncryptedStorage<Path>(
323326
if (!fs.exists(path)) {
324327
val parent = fs.parent(path) ?: return
325328
val name = fs.name(path)
326-
fs.create(parent, name, FileMetadata.FileType.Directory)
329+
fs.createDirectory(fs.joinPath(parent, name))
327330
}
328331
}
329332
}

agents/agents-features/agents-features-memory/src/jvmTest/kotlin/ai/koog/agents/memory/storage/JvmStorageTest.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ai.koog.agents.memory.storage
22

33
import ai.koog.rag.base.files.JVMFileSystemProvider
4+
import ai.koog.rag.base.files.readText
45
import kotlinx.coroutines.test.runTest
56
import org.junit.jupiter.api.Test
67
import org.junit.jupiter.api.io.TempDir
@@ -21,7 +22,7 @@ class JvmStorageTest {
2122
val storage = EncryptedStorage(fs, encryptor)
2223

2324
// Test file creation and writing
24-
val testPath = fs.fromRelativeString(tempDir, "test.txt")
25+
val testPath = fs.joinPath(tempDir, "test.txt")
2526
val testContent = "Hello, World!"
2627
storage.write(testPath, testContent)
2728

@@ -33,23 +34,23 @@ class JvmStorageTest {
3334
assertEquals(testContent, readContent)
3435

3536
// Test directory creation
36-
val dirPath = fs.fromRelativeString(tempDir, "test/nested/dir")
37+
val dirPath = fs.joinPath(tempDir, "test/nested/dir")
3738
storage.createDirectories(dirPath)
3839
assertTrue(storage.exists(dirPath))
3940

4041
// Test file in nested directory
41-
val nestedPath = fs.fromRelativeString(tempDir, "test/nested/dir/nested.txt")
42+
val nestedPath = fs.joinPath(tempDir, "test/nested/dir/nested.txt")
4243
storage.write(nestedPath, testContent)
4344
assertTrue(storage.exists(nestedPath))
4445
assertEquals(testContent, storage.read(nestedPath))
4546

4647
// Test non-existent file
47-
val nonExistentPath = fs.fromRelativeString(tempDir, "non-existent.txt")
48+
val nonExistentPath = fs.joinPath(tempDir, "non-existent.txt")
4849
assertFalse(storage.exists(nonExistentPath))
4950
assertEquals(null, storage.read(nonExistentPath))
5051

5152
// Test encryption
52-
val encryptedContent = fs.read(testPath).decodeToString()
53+
val encryptedContent = fs.readText(testPath)
5354
assertFalse(encryptedContent.contains(testContent), "Content should be encrypted")
5455
}
5556

agents/agents-features/agents-features-snapshot/src/commonMain/kotlin/ai/koog/agents/snapshot/providers/file/FilePersistencyStorageProvider.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package ai.koog.agents.snapshot.providers.file
22

33
import ai.koog.agents.snapshot.feature.AgentCheckpointData
44
import ai.koog.agents.snapshot.providers.PersistencyStorageProvider
5-
import ai.koog.rag.base.files.FileMetadata
65
import ai.koog.rag.base.files.FileSystemProvider
6+
import ai.koog.rag.base.files.createDirectory
7+
import ai.koog.rag.base.files.readText
8+
import ai.koog.rag.base.files.writeText
79
import kotlinx.serialization.json.Json
810

911
/**
@@ -27,9 +29,9 @@ public open class FilePersistencyStorageProvider<Path>(
2729
* Directory where agent checkpoints are stored
2830
*/
2931
private suspend fun checkpointsDir(): Path {
30-
val dir = fs.fromRelativeString(root, "checkpoints")
32+
val dir = fs.joinPath(root, "checkpoints")
3133
if (!fs.exists(dir)) {
32-
fs.create(root, "checkpoints", FileMetadata.FileType.Directory)
34+
fs.createDirectory(dir)
3335
}
3436
return dir
3537
}
@@ -39,9 +41,9 @@ public open class FilePersistencyStorageProvider<Path>(
3941
*/
4042
private suspend fun agentCheckpointsDir(): Path {
4143
val checkpointsDir = checkpointsDir()
42-
val agentDir = fs.fromRelativeString(checkpointsDir, persistenceId)
44+
val agentDir = fs.joinPath(checkpointsDir, persistenceId)
4345
if (!fs.exists(agentDir)) {
44-
fs.create(checkpointsDir, persistenceId, FileMetadata.FileType.Directory)
46+
fs.createDirectory(agentDir)
4547
}
4648
return agentDir
4749
}
@@ -51,7 +53,7 @@ public open class FilePersistencyStorageProvider<Path>(
5153
*/
5254
private suspend fun checkpointPath(checkpointId: String): Path {
5355
val agentDir = agentCheckpointsDir()
54-
return fs.fromRelativeString(agentDir, checkpointId)
56+
return fs.joinPath(agentDir, checkpointId)
5557
}
5658

5759
override suspend fun getCheckpoints(): List<AgentCheckpointData> {
@@ -63,7 +65,7 @@ public open class FilePersistencyStorageProvider<Path>(
6365

6466
return fs.list(agentDir).mapNotNull { path ->
6567
try {
66-
val content = fs.read(path).decodeToString()
68+
val content = fs.readText(path)
6769
json.decodeFromString<AgentCheckpointData>(content)
6870
} catch (_: Exception) {
6971
null
@@ -74,7 +76,7 @@ public open class FilePersistencyStorageProvider<Path>(
7476
override suspend fun saveCheckpoint(agentCheckpointData: AgentCheckpointData) {
7577
val checkpointPath = checkpointPath(agentCheckpointData.checkpointId)
7678
val serialized = json.encodeToString(AgentCheckpointData.serializer(), agentCheckpointData)
77-
fs.write(checkpointPath, serialized.encodeToByteArray())
79+
fs.writeText(checkpointPath, serialized)
7880
}
7981

8082
override suspend fun getLatestCheckpoint(): AgentCheckpointData? {

rag/rag-base/src/commonMain/kotlin/ai/koog/rag/base/files/Extensions.kt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,75 @@ private fun <Path> Path.components(fs: FileSystemProvider.ReadOnly<Path>): List<
2020
}
2121
}.asReversed()
2222
}
23+
24+
/**
25+
* Reads the entire content of a file as a string.
26+
*
27+
* @param path The file path to read.
28+
* @param documentProvider document provider to get unsaved text from.
29+
* @return The file content as a string.
30+
*
31+
* @see [FileSystemProvider.ReadOnly.readBytes]
32+
* @see [DocumentProvider.document]
33+
* @see [DocumentProvider.text]
34+
*/
35+
public suspend fun <Path, Document> FileSystemProvider.ReadOnly<Path>.readText(
36+
path: Path,
37+
documentProvider: DocumentProvider<Path, Document>?
38+
): String {
39+
if (documentProvider != null) {
40+
val document = documentProvider.document(path)
41+
if (document != null) {
42+
return documentProvider.text(document).toString()
43+
}
44+
}
45+
return readBytes(path).decodeToString()
46+
}
47+
48+
/**
49+
* Reads the entire content of a file as a string.
50+
*
51+
* @param path The file path to read.
52+
* @return The file content as a string.
53+
*
54+
* @see [FileSystemProvider.ReadOnly.readBytes]
55+
*/
56+
public suspend fun <Path> FileSystemProvider.ReadOnly<Path>.readText(path: Path): String {
57+
return readBytes(path).decodeToString()
58+
}
59+
60+
/**
61+
* Writes a string to a file, replacing any existing content.
62+
*
63+
* @param path The file path to write to.
64+
* @param content The string content to write.
65+
*
66+
* @see [FileSystemProvider.ReadWrite.writeBytes]
67+
*/
68+
public suspend fun <Path> FileSystemProvider.ReadWrite<Path>.writeText(path: Path, content: String) {
69+
writeBytes(path, content.encodeToByteArray())
70+
}
71+
72+
/**
73+
* Creates a file at the specified [path].
74+
* Parent directories will be created automatically if they don't exist.
75+
*
76+
* @param path The path where the file should be created.
77+
*
78+
* @see [FileSystemProvider.ReadWrite.create]
79+
*/
80+
public suspend fun <Path> FileSystemProvider.ReadWrite<Path>.createFile(path: Path) {
81+
create(path, FileMetadata.FileType.File)
82+
}
83+
84+
/**
85+
* Creates a directory at the specified [path].
86+
* Parent directories will be created automatically if they don't exist.
87+
*
88+
* @param path The path where the directory should be created.
89+
*
90+
* @see [FileSystemProvider.ReadWrite.create]
91+
*/
92+
public suspend fun <Path> FileSystemProvider.ReadWrite<Path>.createDirectory(path: Path) {
93+
create(path, FileMetadata.FileType.Directory)
94+
}

0 commit comments

Comments
 (0)