fix: save uploaded files to recipe-images/ subdir and inject source URL
Build and Deploy / build-and-deploy (push) Successful in 1m49s
Build and Deploy / build-and-deploy (push) Successful in 1m49s
This commit is contained in:
+43
-14
@@ -384,14 +384,15 @@ runFileImport recipeDir config originalName _mimeType b64Text = do
|
||||
Html.ImportError $
|
||||
"Could not decode uploaded file: " <> T.pack err
|
||||
Right content -> do
|
||||
-- Ensure recipe dir exists
|
||||
createDirectoryIfMissing True recipeDir
|
||||
-- Save the original file
|
||||
let sourceFilename = map (\c -> if isAlphaNum c || c == '.' || c == '-' || c == '_' then c else '_') (T.unpack originalName)
|
||||
sourcePath = recipeDir </> sourceFilename
|
||||
-- Save the original file to recipe-images/ subdirectory
|
||||
let imagesDir = recipeDir </> "recipe-images"
|
||||
safeName = map (\c -> if isAlphaNum c || c == '.' || c == '-' || c == '_' then c else '_') (T.unpack originalName)
|
||||
sourcePath = imagesDir </> safeName
|
||||
sourceUrl = "/recipe-images/" <> T.pack safeName
|
||||
createDirectoryIfMissing True imagesDir
|
||||
BS.writeFile sourcePath content
|
||||
putStrLn $ "[roux] saved uploaded file to " <> sourcePath
|
||||
-- Call the LLM pipeline with the saved file
|
||||
-- Call the LLM pipeline with the source URL
|
||||
case rcAnthropic config of
|
||||
Nothing ->
|
||||
pure $
|
||||
@@ -399,7 +400,7 @@ runFileImport recipeDir config originalName _mimeType b64Text = do
|
||||
Html.ImportError $
|
||||
"LLM import is not configured. Add an anthropic.api_key to the config file."
|
||||
Just anthropicConfig -> do
|
||||
result <- runLLMPipeline recipeDir anthropicConfig (Just sourcePath) ""
|
||||
result <- runLLMPipeline recipeDir anthropicConfig (Just sourceUrl) (Just sourcePath) ""
|
||||
case result of
|
||||
Right filename -> pure $ Right filename
|
||||
Left err -> pure $ Left err
|
||||
@@ -418,7 +419,7 @@ runTextImport recipeDir config text = do
|
||||
Html.ImportError $
|
||||
"LLM import is not configured. Add an anthropic.api_key to the config file."
|
||||
Just anthropicConfig ->
|
||||
runLLMPipeline recipeDir anthropicConfig Nothing text
|
||||
runLLMPipeline recipeDir anthropicConfig Nothing Nothing text
|
||||
|
||||
-- | Run the full LLM import pipeline: call convert-to-cooklang -> parse -> save.
|
||||
runLLMPipeline ::
|
||||
@@ -426,19 +427,21 @@ runLLMPipeline ::
|
||||
FilePath ->
|
||||
-- | API config
|
||||
AnthropicConfig ->
|
||||
-- | Just path = uploaded source file to keep
|
||||
-- | Just url = source URL to inject into Cooklang front matter
|
||||
Maybe Text ->
|
||||
-- | Just path = file path for the script (file upload) / Nothing = use text
|
||||
Maybe FilePath ->
|
||||
-- | recipe text (or empty if file)
|
||||
-- | recipe text (used when no file path)
|
||||
Text ->
|
||||
IO (Either Html.ImportError FilePath)
|
||||
runLLMPipeline recipeDir anthropicConfig mSourceFile text = do
|
||||
runLLMPipeline recipeDir anthropicConfig mSourceUrl mFilePath text = do
|
||||
-- Step 1: locate the script
|
||||
script <- findConvertScript
|
||||
-- Step 2: write a temp config for the subprocess
|
||||
let configPath = recipeDir </> ".convert-config.yaml"
|
||||
writeConvertConfig configPath anthropicConfig
|
||||
-- Step 3: build subprocess args
|
||||
let args = case mSourceFile of
|
||||
let args = case mFilePath of
|
||||
Just fp -> ["--config", configPath, "--file", fp]
|
||||
Nothing -> ["--config", configPath, "--text", T.unpack text]
|
||||
putStrLn $ "[roux] running " <> script <> " for LLM import"
|
||||
@@ -456,7 +459,10 @@ runLLMPipeline recipeDir anthropicConfig mSourceFile text = do
|
||||
"Could not convert recipe: " <> errMsg
|
||||
ExitSuccess -> do
|
||||
-- Step 4: parse the Cooklang output to extract the title
|
||||
let cooklangText = TE.decodeUtf8 (LB.toStrict stdoutBytes)
|
||||
let rawCooklang = TE.decodeUtf8 (LB.toStrict stdoutBytes)
|
||||
cooklangText = case mSourceUrl of
|
||||
Just url -> injectSourceUrl rawCooklang url
|
||||
Nothing -> rawCooklang
|
||||
case parseRecipeTitle cooklangText of
|
||||
Nothing -> do
|
||||
putStrLn "[roux] could not extract title from Cooklang output"
|
||||
@@ -469,7 +475,7 @@ runLLMPipeline recipeDir anthropicConfig mSourceFile text = do
|
||||
filepath = recipeDir </> filename
|
||||
-- Step 5: write the .cook file
|
||||
putStrLn $ "[roux] writing imported recipe to " <> filepath
|
||||
LB.writeFile filepath stdoutBytes
|
||||
BS.writeFile filepath (TE.encodeUtf8 cooklangText)
|
||||
putStrLn $ "[roux] successfully imported recipe as " <> filename
|
||||
return $ Right filename
|
||||
|
||||
@@ -500,6 +506,29 @@ writeConvertConfig path ac = do
|
||||
Nothing -> []
|
||||
writeFile path (unlines pairs)
|
||||
|
||||
{- | Insert or update the 'source:' field in Cooklang YAML front matter.
|
||||
If a source field already exists, its value is replaced. Otherwise a new
|
||||
source line is added before the closing '---'.
|
||||
-}
|
||||
injectSourceUrl :: Text -> Text -> Text
|
||||
injectSourceUrl cooklang url =
|
||||
case T.breakOn "---" cooklang of
|
||||
(before, rest) ->
|
||||
let afterFirst = T.drop 3 rest
|
||||
(frontMatter, afterSecond) = T.breakOn "---" afterFirst
|
||||
updated =
|
||||
let (beforeSource, afterSource) = T.breakOn "source:" frontMatter
|
||||
in if T.null afterSource
|
||||
-- No existing source field: append one
|
||||
then
|
||||
let trimmed = T.stripEnd frontMatter
|
||||
in trimmed <> "\nsource: " <> url <> "\n"
|
||||
-- Existing source field: replace its value
|
||||
else
|
||||
let restOfLine = T.drop 1 (T.dropWhile (/= '\n') afterSource)
|
||||
in beforeSource <> "source: " <> url <> restOfLine
|
||||
in before <> "---" <> updated <> "---" <> afterSecond
|
||||
|
||||
{- | Parse the recipe title from Cooklang front matter.
|
||||
Looks for "title: ..." in the YAML front matter block.
|
||||
-}
|
||||
|
||||
Reference in New Issue
Block a user